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:12 UTC
[25/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/asf-site
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}}