You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by sn...@apache.org on 2015/08/25 16:46:14 UTC

[27/45] usergrid git commit: Adding cross-references to Swagger generated docs.

Adding cross-references to Swagger generated docs.


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/9fb9dce5
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/9fb9dce5
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/9fb9dce5

Branch: refs/heads/master
Commit: 9fb9dce59c30756fa2b8150f10923cfa0382f41f
Parents: cd02730
Author: Dave Johnson <sn...@apache.org>
Authored: Tue Aug 25 10:41:28 2015 -0400
Committer: Dave Johnson <sn...@apache.org>
Committed: Tue Aug 25 10:41:28 2015 -0400

----------------------------------------------------------------------
 .../main/groovy/usergrid/ApiDocGenerator.groovy |  189 ++-
 docs/src/main/resources/html/file-end.mustache  |   17 +
 .../src/main/resources/html/file-start.mustache |   33 +
 docs/src/main/resources/html/model.mustache     |   41 +-
 docs/src/main/resources/html/operation.mustache |   11 +-
 docs/src/main/resources/markdown/model.mustache |   50 +-
 docs/src/main/resources/usergrid-swagger.yaml   | 1543 ++++++++----------
 7 files changed, 993 insertions(+), 891 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/9fb9dce5/docs/src/main/groovy/usergrid/ApiDocGenerator.groovy
----------------------------------------------------------------------
diff --git a/docs/src/main/groovy/usergrid/ApiDocGenerator.groovy b/docs/src/main/groovy/usergrid/ApiDocGenerator.groovy
index efc99e9..523cdd4 100644
--- a/docs/src/main/groovy/usergrid/ApiDocGenerator.groovy
+++ b/docs/src/main/groovy/usergrid/ApiDocGenerator.groovy
@@ -5,22 +5,31 @@
 // Depdency management with Grape:
 //    http://docs.groovy-lang.org/latest/html/documentation/grape.html
 
-package usergrid;
+package usergrid
 
 @Grab(group = 'io.swagger', module = 'swagger-parser', version = '1.0.8')
 @Grab(group = 'io.swagger', module = 'swagger-compat-spec-parser', version = '1.0.8')
 @Grab(group = 'com.github.spullara.mustache.java', module = 'compiler', version = '0.8.18-SNAPSHOT')
+@Grab(group = 'org.slf4j', module = 'slf4j-simple', version = '1.7.12')
 
-import io.swagger.parser.*;
-import io.swagger.models.*;
-import com.github.mustachejava.*
-import org.apache.commons.lang3.RandomStringUtils;
+import com.github.mustachejava.DefaultMustacheFactory
+import io.swagger.models.Model
+import io.swagger.models.Operation
+import io.swagger.models.parameters.BodyParameter
+import io.swagger.models.parameters.RefParameter
+import io.swagger.models.properties.RefProperty
+import io.swagger.parser.*
+import org.apache.commons.lang3.RandomStringUtils
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
 
 
 /**
  * Generates Usergrid API docs from Swagger in Markdown format.
  */
 public class ApiDocGenerator {
+   
+    static Logger logger = LoggerFactory.getLogger(ApiDocGenerator.class);
     
     def parser = new SwaggerParser();
     def swagger = parser.read("src/main/resources/usergrid-swagger.yaml");
@@ -39,8 +48,14 @@ public class ApiDocGenerator {
     
     def allTags = [];
     def allModels = [];
-
-
+    def urlOpsBySchema = [:];
+    def modelsBySchema = [:];
+    
+    def tagsToUrlOps = new TreeMap();
+    
+    def definitions = new TreeMap();
+    def subTypes = new TreeMap();
+    
     public ApiDocGenerator() {
         writer = new OutputStreamWriter(new FileOutputStream("rest-endpoints/api-docs.md"));
         operationTemplate = mf.compile(
@@ -61,59 +76,66 @@ public class ApiDocGenerator {
     }
     
     def generate() {
-       
-        // build up scope and generate via Mustache template
-        
-        // organize methods by tag
-        def tagsToUrlOps = new TreeMap();
+      
+        // build various hashmaps and lists to help organize and cross-reference content
         
         swagger.getPaths().each { pathEntry ->
             def url = pathEntry.key;
             def path = pathEntry.value;
            
             if (path.get != null) {
-                addOperation(tagsToUrlOps, "GET", url, path.get);
+                addOperation("GET", url, path.get);
             }
             if (path.post != null) {
-                addOperation(tagsToUrlOps, "POST", url, path.post);
+                addOperation("POST", url, path.post);
             }
             if (path.put != null) {
-                addOperation(tagsToUrlOps, "PUT", url, path.put);
+                addOperation("PUT", url, path.put);
             }
             if (path.delete != null) {
-                addOperation(tagsToUrlOps, "DELETE", url, path.delete);
+                addOperation("DELETE", url, path.delete);
             }
         };
-
         tagsToUrlOps.each { entry -> allTags.add(entry.key) };
 
-        def definitions = new TreeMap();
         swagger.getDefinitions().each { entry ->
-            definitions.put( entry.key, entry.value );
+            addModel( entry.key, entry.value );
         };
         definitions.each { entry -> allModels.add(entry.key); };
-       
-        // generate
+        
+        // generate the doc
         
         generateFileStart();
+        
         generateMethodsSectionTitle();
+        
         tagsToUrlOps.each { entry -> 
             def tag = entry.key;
             def urlOps = entry.value;
             generateMethodsTitle(tag);
             urlOps.each { urlOp -> formatOperation( urlOp ); };
         };
-        
+
         generateModelsTitle();
+        
         definitions.each { entry ->
             def name = entry.key;
             def model = entry.value;
             formatModel( name, model );
         };
+        
+        generateSubTypesTitle();
+        
+        subTypes.each { entry ->
+            def name = entry.key;
+            def model = entry.value;
+            formatModel( name, model );
+        };
+        
         generateFileEnd();
     }
     
-    def addOperation( Map tagsToUrlOps, String method, String url, Operation operation ) {
+    def addOperation( String method, String url, Operation operation ) {
        
         // assume each operation has one tag
         def tag = operation.tags[0];
@@ -123,12 +145,67 @@ public class ApiDocGenerator {
             urlOps = [];
             tagsToUrlOps[tag] = urlOps;
         }
-
+        
         def urlOp = new HashMap();
         urlOp.url = url;
         urlOp.method = method;
         urlOp.operation = operation;
+        urlOp.opId = RandomStringUtils.randomAlphanumeric(10);
         urlOps.add(urlOp);
+
+        operation.getResponses().each { responseEntry ->
+
+            if (responseEntry.value.schema != null) {
+
+                if (responseEntry.key.equals("200")) {
+                    def list = urlOpsBySchema[responseEntry.value.schema.ref];
+                    if (list == null) {
+                        list = new HashSet();
+                        urlOpsBySchema[responseEntry.value.schema.ref] = list;
+                    }
+                    list.add(urlOp);
+                }
+            }
+        };
+
+        operation.getParameters().each { param ->
+            
+            if ( param instanceof BodyParameter && param.schema != null ) {
+                def list = urlOpsBySchema[param.schema.ref];
+                if (list == null) {
+                    list = new HashSet();
+                    urlOpsBySchema[param.schema.ref] = list;
+                }
+                list.add(urlOp);
+            }
+        };
+        
+    }
+    
+    def addModel(name, model) {
+       
+        if (urlOpsBySchema[name] != null || name.equals("Error")) {
+            definitions.put(name, model);
+        } else if (modelsBySchema[name] != null ) {
+            subTypes.put(name, model);
+        } else {
+            logger.error("${name} omitted because it is not referenced by any API path or definition.");
+        }
+        
+        model.properties.each { propertyEntity ->
+            
+            if (propertyEntity.value instanceof RefProperty ) {
+                def list = modelsBySchema[ propertyEntity.value.ref ];
+                if ( list == null ) {
+                    list = [];
+                    modelsBySchema[ propertyEntity.value.ref ] = list;
+                }
+                def modelNames = [:];
+                modelNames.refName = name;
+                modelNames.refName_lc = name.toLowerCase();
+                list.add(modelNames);
+            }
+        };
     }
 
     def formatOperation( urlOp ) {
@@ -150,7 +227,17 @@ public class ApiDocGenerator {
             // if parameter has a schema, assume that it is a reference
             if ( responseEntry.value.schema != null) {
                 response.schema = responseEntry.value.schema.ref;
-                response.schemaAnchor = responseEntry.value.schema.ref.toLowerCase();
+                response.schemaAnchor = responseEntry.value.schema.ref;
+               
+                // keep track of paths that use each schema definition
+                if ( response.status.equals("200") ) {
+                    def list = urlOpsBySchema[response.schema];
+                    if ( list == null ) {
+                        list = [];
+                        urlOpsBySchema[response.schema] = list;
+                    }
+                    list.add(urlOp);
+                }
             }
             responses.add(response);
         }
@@ -159,15 +246,26 @@ public class ApiDocGenerator {
         op.getParameters().each { parameter -> 
             def param = [:];
             param.name = parameter.name;
+            param.name_lc = parameter.name.toLowerCase();
             param.required = parameter.required;
             param.description = parameter.description;
             param.in = parameter.in;
             
             // assume that body parameters have a schema that is a reference
-            if (parameter.in == "body" && parameter.schema != null) {
+            if (parameter.in == "body") {
+                
                 param.schemaRef = parameter.schema.ref;
                 param.schemaAnchor = parameter.schema.ref.toLowerCase();
-            } else { // if (parameter.in == "path") {
+
+                // keep track of paths that use each schema definition
+                def list = urlOpsBySchema[parameter.schema.ref];
+                if ( list == null ) {
+                    list = [];
+                    urlOpsBySchema[parameter.schema.ref] = list;
+                }
+                list.add(urlOp);
+
+            } else if ( !(parameter instanceof RefParameter) ) {
                 param.type = parameter.type;
             }
             params.add(param);
@@ -181,14 +279,21 @@ public class ApiDocGenerator {
         scope.tags = op.getTags();
         scope.responses = responses;
         scope.parameters = params;
-        scope.opid = RandomStringUtils.randomAlphanumeric(10);
-       
+        scope.opId = urlOp.opId;
+        
         operationTemplate.execute(writer, scope);
         writer.flush();
     }
     
     def formatModel(String name, Model model) {
 
+        def scope = [:];
+        scope.referrers = urlOpsBySchema[name];
+        scope.modelRefs = modelsBySchema[name];
+        scope.hasReferrers = scope.referrers != null && !scope.referrers.isEmpty();
+        scope.hasParamRefs = scope.paramRefs != null && !scope.paramRefs.isEmpty();
+        scope.hasModelRefs = scope.modelRefs != null && !scope.modelRefs.isEmpty();
+        
         // put properties in array form, mustache doesn't play nice with associative arrays
         def props = [];
         model.getProperties().each { property -> 
@@ -201,12 +306,17 @@ public class ApiDocGenerator {
             prop.readOnly = property.value.readOnly;
             prop.required = property.value.required;
             prop.position = property.value.position;
+            if ( property.value instanceof RefProperty ) {
+                prop.ref = property.value.ref;
+                prop.ref_lc = property.value.ref.toLowerCase();
+            }
             props.add(prop);   
         };
-        def scope = [:];
         scope.name = name;
+        scope.name_lc = name.toLowerCase();
+        scope.description = model.description;
         scope.properties = props;
-        scope.modelid = RandomStringUtils.randomAlphanumeric(10);
+       
         modelTemplate.execute(writer, scope);
         writer.flush(); 
     }
@@ -217,7 +327,7 @@ public class ApiDocGenerator {
         allTags.each{ tag -> 
             def atag = [:];
             atag.name = tag;
-            atag.link = tag.toLowerCase();
+            atag.link = tag;
             tags.add(atag);
         };
         scope.tags = tags;
@@ -239,7 +349,12 @@ public class ApiDocGenerator {
     
     def generateModelsTitle() {
         writer.println "\n## Models";
-        writer.println "Properties for Usergrid default entities.";
+        writer.println "This section lists the properties for the Usergrid Default Entities:";
+    }
+
+    def generateSubTypesTitle() {
+        writer.println "\n## Sub-Types";
+        writer.println "This section lists the properties for sub-types used in Usergrid Default Entities.";
     }
     
     def generateFileEnd() {
@@ -289,9 +404,15 @@ class HtmlApiDocGenerator extends ApiDocGenerator {
     def generateModelsTitle() {
         writer.println "<a name='models'></a>";
         writer.println "<br><h1>Default Entity Models</h1>" +
-                "<p>This section lists the properties for the following Usergrid Default Entities:</p>";
+                "<p>This section lists the properties for the following Usergrid Default Entities.</p>";
     }
 
+    def generateSubTypesTitle() {
+        writer.println "<a name='subtypes'></a>";
+        writer.println "<br><h1>Sub-types Referenced By Models</h1>" +
+                "<p>This section lists the properties for sub-types used in Usergrid Default Entities.</p>";
+    }
+    
     def generateFileEnd() {
         def scope = [:];
         fileEndTemplate.execute(writer, scope);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/9fb9dce5/docs/src/main/resources/html/file-end.mustache
----------------------------------------------------------------------
diff --git a/docs/src/main/resources/html/file-end.mustache b/docs/src/main/resources/html/file-end.mustache
index 28d75b5..77b0b67 100644
--- a/docs/src/main/resources/html/file-end.mustache
+++ b/docs/src/main/resources/html/file-end.mustache
@@ -8,6 +8,23 @@
     
 <!-- Latest compiled and minified JavaScript -->
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
+    
+<script>
+if ("onhashchange" in window) {
+    window.onhashchange = function () {
+        var hash = window.location.hash;
+        if ( hash.substring(0,1) != "/") {
+            hash = hash.substring(1, hash.length);
+
+            $("#collapse-" + hash).removeClass("collapse");
+            $("a[href=#collapse-" + hash + "]").removeClass("collapsed");
+
+            $("#" + hash).removeClass("collapse");
+            $("a[href=#" + hash + "]").removeClass("collapsed");
+        }
+    }
+}
+</script>
 
 </body>
 </html>

http://git-wip-us.apache.org/repos/asf/usergrid/blob/9fb9dce5/docs/src/main/resources/html/file-start.mustache
----------------------------------------------------------------------
diff --git a/docs/src/main/resources/html/file-start.mustache b/docs/src/main/resources/html/file-start.mustache
index 642bf42..9951f3c 100644
--- a/docs/src/main/resources/html/file-start.mustache
+++ b/docs/src/main/resources/html/file-start.mustache
@@ -10,6 +10,39 @@
 
     <link rel="stylesheet" href="api-docs.css">
     
+    <style type="text/css">
+        .panel-default > .panel-heading.GET{
+            background-color: rgba(54, 180, 255, 0.42);
+            background-image: none;
+        }
+        .panel-default > .panel-heading.POST {
+            background-color: rgba(0, 128, 0, 0.38);
+            background-image: none;
+        }
+        .panel-default > .panel-heading.PUT{
+            background-color: rgba(255, 165, 0, 0.41);
+            background-image: none;
+        }
+        .panel-default > .panel-heading.DELETE {
+            background-color: rgba(255, 0, 0, 0.29);
+            background-image: none;
+        }
+
+        .panel-heading a.collapse-button:after {
+            font-family:'Glyphicons Halflings';
+            content:"\e114";
+            float: right;
+            color: grey;
+        }
+        .panel-heading a.collapse-button.collapsed:after {
+            content:"\e080";
+        }
+
+        table td {
+            font-size: small;
+        }
+    </style>
+    
 </head>
 <body>
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/9fb9dce5/docs/src/main/resources/html/model.mustache
----------------------------------------------------------------------
diff --git a/docs/src/main/resources/html/model.mustache b/docs/src/main/resources/html/model.mustache
index 7cb45ef..9077e4e 100644
--- a/docs/src/main/resources/html/model.mustache
+++ b/docs/src/main/resources/html/model.mustache
@@ -1,20 +1,23 @@
 
+
 <div>
-    <a name="{{name}}"/>
+    <a name="{{name_lc}}"/>
 </div>
 
 <div class="panel panel-default">
     
     <div class="panel-heading">
         <h2 class="panel-title model-heading">{{name}}
-            <a data-toggle="collapse" data-target="#collapse-{{modelid}}"
-               href="#collapse-{{modelid}}" class="collapsed collapse-button"> </a>
+            <a data-toggle="collapse" data-target="#collapse-{{name}}"
+               href="#collapse-{{name}}" class="collapsed collapse-button"> </a>
         </h2>
     </div>
     
-    <div id="collapse-{{modelid}}" class="panel-body">
+    <div id="collapse-{{name}}" class="panel-body collapse">
+        
+        <p>{{description}}</p>
         
-        <h3>Properties</h3>
+        <p><b>Properties</b></p>
 
         <table width="80%" class="table table-striped">
             <tr>
@@ -26,13 +29,39 @@
             {{#properties}}
                 <tr>
                     <td>{{name}}</td>
-                    <td>{{type}}</td>
+                    <td>
+                        {{#ref}}<a href="#{{ref_lc}}">{{ref}}</a>{{/ref}}
+                        {{^ref}}{{type}}{{/ref}}
+                    </td>
                     <td>{{description}}</td>
                     <td>{{required}}</td>
                 </tr>
             {{/properties}}
         </table>
 
+        {{#hasReferrers}}
+        <p><b>Referring API Paths</b></p>
+        <table width="80%" class="table table-striped">
+            {{#referrers}}
+            <tr>
+                <td><p><a href="#op-{{opId}}">{{url}}</a></p></td>
+            </tr>
+            {{/referrers}}
+        </table>
+        {{/hasReferrers}}
+
+        {{#hasModelRefs}}
+            <p><b>Referring Definitions</b></p>
+            <table width="80%" class="table table-striped">
+                {{#modelRefs}}
+                    <tr>
+                        <td><p><a href="#{{refName_lc}}">{{refName}}</a></p></td>
+                    </tr>
+                {{/modelRefs}}
+            </table>
+        {{/hasModelRefs}}
+
     </div>
     
 </div>
+

http://git-wip-us.apache.org/repos/asf/usergrid/blob/9fb9dce5/docs/src/main/resources/html/operation.mustache
----------------------------------------------------------------------
diff --git a/docs/src/main/resources/html/operation.mustache b/docs/src/main/resources/html/operation.mustache
index 6e15b1f..27b7ab5 100644
--- a/docs/src/main/resources/html/operation.mustache
+++ b/docs/src/main/resources/html/operation.mustache
@@ -1,14 +1,16 @@
 
+<a name="op-{{opId}}"/>
+
 <div class="panel panel-default">
     
     <div class="panel-heading {{method}}">
         <h2 class="panel-title {{method}}-heading">{{method}} {{url}} 
-            <a data-toggle="collapse" data-target="#collapse-{{opid}}"
-                href="#collapse-{{opid}}" class="collapsed collapse-button"> </a>
+            <a data-toggle="collapse" data-target="#op-{{opId}}"
+                href="#op-{{opId}}" class="collapsed collapse-button"> </a>
         </h2>
     </div>
     
-    <div id="collapse-{{opid}}" class="panel-body collapse">
+    <div id="op-{{opId}}" class="panel-body collapse">
 
         <p>{{description}}</p>
 
@@ -17,8 +19,7 @@
         <ul>
             {{#parameters}}
                 <li>
-                    <b>{{name}}</b> ({{#type}}{{type}}{{/type}}{{#schemaRef}}
-                    <a href="#{{schemaAnchor}}">{{schemaRef}}</a>{{/schemaRef}}) <br>
+                    <b>{{name}}</b> ({{#type}}{{type}}{{/type}}{{#schemaRef}}<a href="#{{schemaAnchor}}">{{schemaRef}}</a>{{/schemaRef}})<br>
                     {{description}} (Specified in {{in}}).
                 </li>
             {{/parameters}}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/9fb9dce5/docs/src/main/resources/markdown/model.mustache
----------------------------------------------------------------------
diff --git a/docs/src/main/resources/markdown/model.mustache b/docs/src/main/resources/markdown/model.mustache
index b58788b..2a41a00 100644
--- a/docs/src/main/resources/markdown/model.mustache
+++ b/docs/src/main/resources/markdown/model.mustache
@@ -1,21 +1,43 @@
 
 ### {{name}}
 
+{{description}}
+
 __Properties__ 
 
 <table width="80%" class="usergrid-table">
-    <tr>
-        <th>Name</th>
-        <th>Type</th>
-        <th>Description</th>
-        <th>Required</th>
-    </tr>
-    {{#properties}}
-    <tr>
-        <td>{{name}}</td>
-        <td>{{type}}</td>
-        <td>{{description}}</td>
-        <td>{{required}}</td>
-    </tr>
-    {{/properties}}
+  <tr>
+      <th>Name</th>
+      <th>Type</th>
+      <th>Description</th>
+      <th>Required</th>
+  </tr>
+  {{#properties}}
+  <tr>
+      <td>{{name}}</td>
+      <td>
+          {{#ref}} [{{ref}}](#{{ref_lc}}) {{/ref}} {{^ref}}{{type}}{{/ref}}
+      </td>
+      <td>{{description}}</td>
+      <td>{{required}}</td>
+  </tr>
+  {{/properties}}
 </table>
+
+{{#hasReferrers}}
+__Referring API Paths__
+  
+{{#referrers}}
+* [{{url}}](#op-{{opId}})
+{{/referrers}}
+      
+{{/hasReferrers}}
+
+{{#hasModelRefs}}
+__Referring Definitions__
+  
+{{#modelRefs}}
+* [{{refName}}](#{{refName_lc}})
+{{/modelRefs}}
+      
+{{/hasModelRefs}}