andrea
Committed by Gerrit Code Review

[ONOS-2706] Insert Support for REST API model schema in Swagger UI

Change-Id: Iad158d90f203101fd23328f30f2507b7677a1997
......@@ -51,6 +51,7 @@ public class DhcpWebResource extends AbstractWebResource {
* Shows lease, renewal and rebinding times in seconds.
*
* @return 200 OK
* @rsModel DhcpConfigGet
*/
@GET
@Path("config")
......@@ -77,14 +78,13 @@ public class DhcpWebResource extends AbstractWebResource {
final Map<HostId, IpAssignment> intents = service.listMapping();
ArrayNode arrayNode = root.putArray("mappings");
intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode()
.put("host", i.getKey().toString())
.put("ip", i.getValue().ipAddress().toString())));
.put("host", i.getKey().toString())
.put("ip", i.getValue().ipAddress().toString())));
return ok(root.toString()).build();
}
/**
* Get all available IPs.
* Shows all the IPs in the free pool of the DHCP Server.
......@@ -106,6 +106,7 @@ public class DhcpWebResource extends AbstractWebResource {
* Post a new static MAC/IP binding.
* Registers a static binding to the DHCP server, and displays the current set of bindings.
*
* @rsModel DhcpConfigPut
* @param stream JSON stream
* @return 200 OK
*/
......@@ -114,7 +115,6 @@ public class DhcpWebResource extends AbstractWebResource {
@Consumes(MediaType.APPLICATION_JSON)
public Response setMapping(InputStream stream) {
ObjectNode root = mapper().createObjectNode();
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
JsonNode macID = jsonTree.get("mac");
......@@ -122,16 +122,18 @@ public class DhcpWebResource extends AbstractWebResource {
if (macID != null && ip != null) {
if (!service.setStaticMapping(MacAddress.valueOf(macID.asText()),
Ip4Address.valueOf(ip.asText()), false, Lists.newArrayList())) {
throw new IllegalArgumentException("Static Mapping Failed. The IP maybe unavailable.");
Ip4Address.valueOf(ip.asText()),
false, Lists.newArrayList())) {
throw new IllegalArgumentException("Static Mapping Failed. " +
"The IP maybe unavailable.");
}
}
final Map<HostId, IpAssignment> intents = service.listMapping();
ArrayNode arrayNode = root.putArray("mappings");
intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode()
.put("host", i.getKey().toString())
.put("ip", i.getValue().ipAddress().toString())));
.put("host", i.getKey().toString())
.put("ip", i.getValue().ipAddress().toString())));
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
......@@ -157,8 +159,8 @@ public class DhcpWebResource extends AbstractWebResource {
final Map<HostId, IpAssignment> intents = service.listMapping();
ArrayNode arrayNode = root.putArray("mappings");
intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode()
.put("host", i.getKey().toString())
.put("ip", i.getValue().ipAddress().toString())));
.put("host", i.getKey().toString())
.put("ip", i.getValue().ipAddress().toString())));
return ok(root.toString()).build();
}
......
{
"type": "object",
"required": [
"leaseTime",
"renewalTime",
"rebindingTime"
],
"properties": {
"leaseTime": {
"type": "integer",
"format": "int64",
"example": "250"
},
"renewalTime": {
"type": "integer",
"format": "int64",
"example": "250"
},
"rebindingTime": {
"type": "integer",
"format": "int64",
"example": "250"
}
}
}
{
"type": "object",
"required": [
"mac",
"ip"],
"properties": {
"mac": {
"type": "String",
"example": "be:48:89:d5:75:59"
},
"ip": {
"type": "String",
"example": "10.128.12.4"
}
}
}
......@@ -49,7 +49,8 @@
<version>2.10.1</version>
<configuration>
<show>package</show>
<excludePackageNames>@external-excludes</excludePackageNames>
<excludePackageNames>@external-excludes
</excludePackageNames>
<docfilessubdirs>true</docfilessubdirs>
<doctitle>ONOS Java API (1.4.0-SNAPSHOT)</doctitle>
<groups>
......@@ -58,7 +59,8 @@
<packages>@external-apis</packages>
</group>
<group>
<title>Incubator for Network Model &amp; Services</title>
<title>Incubator for Network Model &amp; Services
</title>
<packages>@external-incubator-apis</packages>
</group>
<group>
......@@ -66,6 +68,13 @@
<packages>@utils</packages>
</group>
</groups>
<tags>
<tag>
<name>rsModel</name>
<placement>m</placement>
<head>Json model for REST api:</head>
</tag>
</tags>
</configuration>
</plugin>
</plugins>
......
......@@ -51,14 +51,16 @@
<show>package</show>
<docfilessubdirs>true</docfilessubdirs>
<doctitle>ONOS Java API (1.4.0-SNAPSHOT)</doctitle>
<excludePackageNames>@internal-excludes</excludePackageNames>
<excludePackageNames>@internal-excludes
</excludePackageNames>
<groups>
<group>
<title>Network Model &amp; Services</title>
<packages>@internal-apis</packages>
</group>
<group>
<title>Incubator for Network Model &amp; Services</title>
<title>Incubator for Network Model &amp; Services
</title>
<packages>@internal-incubator-apis</packages>
</group>
<group>
......@@ -70,7 +72,9 @@
<packages>@internal-stores</packages>
</group>
<group>
<title>Incubator for Core Subsystems &amp; Distributed Stores</title>
<title>Incubator for Core Subsystems &amp;
Distributed Stores
</title>
<packages>@internal-incubator</packages>
</group>
<group>
......@@ -114,10 +118,18 @@
<packages>@internal-apps</packages>
</group>
<group>
<title>Test Instrumentation &amp; Applications</title>
<title>Test Instrumentation &amp; Applications
</title>
<packages>@internal-test-apps</packages>
</group>
</groups>
<tags>
<tag>
<name>rsModel</name>
<placement>m</placement>
<head>Json model for REST api:</head>
</tag>
</tags>
</configuration>
</plugin>
</plugins>
......
......@@ -39,6 +39,7 @@
<description>Open Network Operating System root project</description>
<modules>
<module>tools/package/maven-plugin</module>
<module>utils</module>
<module>core</module>
<module>web</module>
......@@ -545,7 +546,20 @@
<rerunFailingTestsCount>1</rerunFailingTestsCount>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<configuration>
<tags>
<tag>
<name>rsModel</name>
<placement>m</placement>
<head>Json model for REST api:</head>
</tag>
</tags>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
......@@ -631,8 +645,8 @@
<plugin>
<groupId>org.onosproject</groupId>
<artifactId>onos-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<version>1.6-SNAPSHOT</version>
<executions>
<execution>
<id>cfg</id>
<phase>generate-resources</phase>
......
......@@ -85,6 +85,11 @@
<artifactId>jackson-annotations</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<build>
......
......@@ -18,6 +18,7 @@ package org.onosproject.maven;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Charsets;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.thoughtworks.qdox.JavaProjectBuilder;
......@@ -124,11 +125,13 @@ public class OnosSwaggerMojo extends AbstractMojo {
ObjectNode root = initializeRoot();
ArrayNode tags = mapper.createArrayNode();
ObjectNode paths = mapper.createObjectNode();
ObjectNode definitions = mapper.createObjectNode();
root.set("tags", tags);
root.set("paths", paths);
root.set("definitions", definitions);
builder.getClasses().forEach(jc -> processClass(jc, paths, tags));
builder.getClasses().forEach(jc -> processClass(jc, paths, tags, definitions));
if (paths.size() > 0) {
getLog().info("Generating ONOS REST API documentation...");
......@@ -172,7 +175,7 @@ public class OnosSwaggerMojo extends AbstractMojo {
// Checks whether javaClass has a path tag associated with it and if it does
// processes its methods and creates a tag for the class on the root
void processClass(JavaClass javaClass, ObjectNode paths, ArrayNode tags) {
void processClass(JavaClass javaClass, ObjectNode paths, ArrayNode tags, ObjectNode definitions) {
// If the class does not have a Path tag then ignore it
JavaAnnotation annotation = getPathAnnotation(javaClass);
if (annotation == null) {
......@@ -199,7 +202,7 @@ public class OnosSwaggerMojo extends AbstractMojo {
ArrayNode tagArray = mapper.createArrayNode();
tagArray.add(tagPath);
processAllMethods(javaClass, resourcePath, paths, tagArray);
processAllMethods(javaClass, resourcePath, paths, tagArray, definitions);
}
private JavaAnnotation getPathAnnotation(JavaClass javaClass) {
......@@ -211,7 +214,7 @@ public class OnosSwaggerMojo extends AbstractMojo {
// Checks whether a class's methods are REST methods and then places all the
// methods under a specific path into the paths node
private void processAllMethods(JavaClass javaClass, String resourcePath,
ObjectNode paths, ArrayNode tagArray) {
ObjectNode paths, ArrayNode tagArray, ObjectNode definitions) {
// map of the path to its methods represented by an ObjectNode
Map<String, ObjectNode> pathMap = new HashMap<>();
......@@ -221,7 +224,7 @@ public class OnosSwaggerMojo extends AbstractMojo {
if (name.equals(POST) || name.equals(GET) || name.equals(DELETE) || name.equals(PUT)) {
// substring(12) removes "javax.ws.rs."
String method = annotation.getType().toString().substring(12).toLowerCase();
processRestMethod(javaMethod, method, pathMap, resourcePath, tagArray);
processRestMethod(javaMethod, method, pathMap, resourcePath, tagArray, definitions);
}
});
});
......@@ -236,9 +239,10 @@ public class OnosSwaggerMojo extends AbstractMojo {
private void processRestMethod(JavaMethod javaMethod, String method,
Map<String, ObjectNode> pathMap,
String resourcePath, ArrayNode tagArray) {
String resourcePath, ArrayNode tagArray, ObjectNode definitions) {
String fullPath = resourcePath, consumes = "", produces = "",
comment = javaMethod.getComment();
DocletTag tag = javaMethod.getTagByName("rsModel");
for (JavaAnnotation annotation : javaMethod.getAnnotations()) {
String name = annotation.getType().getName();
if (name.equals(PATH)) {
......@@ -256,12 +260,19 @@ public class OnosSwaggerMojo extends AbstractMojo {
methodNode.set("tags", tagArray);
addSummaryDescriptions(methodNode, comment);
processParameters(javaMethod, methodNode);
addJsonSchemaDefinition(definitions, tag);
addJsonSchemaDefinition(definitions, tag);
processParameters(javaMethod, methodNode, method, tag);
processConsumesProduces(methodNode, "consumes", consumes);
processConsumesProduces(methodNode, "produces", produces);
addResponses(methodNode);
if (tag == null || ((method.toLowerCase().equals("post") || method.toLowerCase().equals("put"))
&& !(tag.getParameters().size() > 1))) {
addResponses(methodNode, tag, false);
} else {
addResponses(methodNode, tag, true);
}
ObjectNode operations = pathMap.get(fullPath);
if (operations == null) {
......@@ -273,6 +284,24 @@ public class OnosSwaggerMojo extends AbstractMojo {
}
}
private void addJsonSchemaDefinition(ObjectNode definitions, DocletTag tag) {
File definitionsDirectory = new File(srcDirectory + "/src/main/resources/definitions");
if (tag != null) {
tag.getParameters().stream().forEach(param -> {
try {
File config = new File(definitionsDirectory.getAbsolutePath() + "/"
+ param + ".json");
String lines = Files.readLines(config, Charsets.UTF_8).stream().reduce((t, u) -> t + u).
get();
definitions.putPOJO(param, lines);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
private void processConsumesProduces(ObjectNode methodNode, String type, String io) {
if (!io.equals("")) {
ArrayNode array = mapper.createArrayNode();
......@@ -299,13 +328,19 @@ public class OnosSwaggerMojo extends AbstractMojo {
// Temporary solution to add responses to a method
// TODO Provide annotations in the web resources for responses and parse them
private void addResponses(ObjectNode methodNode) {
private void addResponses(ObjectNode methodNode, DocletTag tag, boolean responseJson) {
ObjectNode responses = mapper.createObjectNode();
methodNode.set("responses", responses);
ObjectNode success = mapper.createObjectNode();
success.put("description", "successful operation");
responses.set("200", success);
if (tag != null && responseJson) {
ObjectNode schema = mapper.createObjectNode();
tag.getParameters().stream().forEach(
param -> schema.put("$ref", "#/definitions/" + param));
success.set("schema", schema);
}
ObjectNode defaultObj = mapper.createObjectNode();
defaultObj.put("description", "Unexpected error");
......@@ -329,7 +364,7 @@ public class OnosSwaggerMojo extends AbstractMojo {
}
// Processes parameters of javaMethod and enters the proper key-values into the methodNode
private void processParameters(JavaMethod javaMethod, ObjectNode methodNode) {
private void processParameters(JavaMethod javaMethod, ObjectNode methodNode, String method, DocletTag tag) {
ArrayNode parameters = mapper.createArrayNode();
methodNode.set("parameters", parameters);
boolean required = true;
......@@ -346,7 +381,8 @@ public class OnosSwaggerMojo extends AbstractMojo {
if (pathType != null) { //the parameter is a path or query parameter
individualParameterNode.put("name",
pathType.getNamedParameter("value").toString().replace("\"", ""));
pathType.getNamedParameter("value")
.toString().replace("\"", ""));
if (pathType.getType().getName().equals(PATH_PARAM)) {
individualParameterNode.put("in", "path");
} else if (pathType.getType().getName().equals(QUERY_PARAM)) {
......@@ -357,10 +393,16 @@ public class OnosSwaggerMojo extends AbstractMojo {
individualParameterNode.put("name", annotationName);
individualParameterNode.put("in", "body");
// TODO add actual hardcoded schemas and a type
// body parameters must have a schema associated with them
ArrayNode schema = mapper.createArrayNode();
individualParameterNode.set("schema", schema);
// Adds the reference to the Json model for the input
// that goes in the post or put operation
if (tag != null && (method.toLowerCase().equals("post") ||
method.toLowerCase().equals("put"))) {
ObjectNode schema = mapper.createObjectNode();
tag.getParameters().stream().forEach(param -> {
schema.put("$ref", "#/definitions/" + param);
});
individualParameterNode.set("schema", schema);
}
}
for (DocletTag p : javaMethod.getTagsByName("param")) {
if (p.getValue().contains(annotationName)) {
......