X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=restconf%2Fsal-rest-docgen%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fsal%2Frest%2Fdoc%2Fimpl%2FBaseYangSwaggerGenerator.java;h=b9f5290eff16f9be2ace3841f469cb13d52bf3d3;hb=1e174be6002940e17aee31d15f8914273d30f25c;hp=fd750b45407860841024ad5ba193f24720e1804d;hpb=b63c5edc0f896e131846e975340cef7e9f1e0f36;p=netconf.git diff --git a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/BaseYangSwaggerGenerator.java b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/BaseYangSwaggerGenerator.java index fd750b4540..b9f5290eff 100644 --- a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/BaseYangSwaggerGenerator.java +++ b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/BaseYangSwaggerGenerator.java @@ -11,27 +11,23 @@ import static org.opendaylight.netconf.sal.rest.doc.util.RestDocgenUtil.resolveP import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.base.Preconditions; import java.io.IOException; import java.net.URI; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; import java.util.Collection; import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.ws.rs.core.UriInfo; -import org.json.JSONException; -import org.json.JSONObject; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; import org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder; import org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.Delete; import org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.Get; @@ -44,10 +40,13 @@ import org.opendaylight.netconf.sal.rest.doc.swagger.Parameter; import org.opendaylight.netconf.sal.rest.doc.swagger.Resource; import org.opendaylight.netconf.sal.rest.doc.swagger.ResourceList; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; @@ -55,30 +54,40 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class BaseYangSwaggerGenerator { +public abstract class BaseYangSwaggerGenerator { private static final Logger LOG = LoggerFactory.getLogger(BaseYangSwaggerGenerator.class); protected static final String API_VERSION = "1.0.0"; protected static final String SWAGGER_VERSION = "1.2"; - protected static final String RESTCONF_CONTEXT_ROOT = "restconf"; static final String MODULE_NAME_SUFFIX = "_module"; - protected static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); private final ModelGenerator jsonConverter = new ModelGenerator(); // private Map MODULE_DOC_CACHE = new HashMap<>() private final ObjectMapper mapper = new ObjectMapper(); + private final DOMSchemaService schemaService; - protected BaseYangSwaggerGenerator() { - mapper.registerModule(new JsonOrgModule()); - mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + protected BaseYangSwaggerGenerator(Optional schemaService) { + this.schemaService = schemaService.orElse(null); + this.mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + } + + public DOMSchemaService getSchemaService() { + return schemaService; + } + + public ResourceList getResourceListing(final UriInfo uriInfo) { + final SchemaContext schemaContext = schemaService.getGlobalContext(); + Preconditions.checkState(schemaContext != null); + return getResourceListing(uriInfo, schemaContext, ""); } /** * Return list of modules converted to swagger compliant resource list. */ - public ResourceList getResourceListing(final UriInfo uriInfo, final SchemaContext schemaContext, final String context) { + public ResourceList getResourceListing(final UriInfo uriInfo, final SchemaContext schemaContext, + final String context) { final ResourceList resourceList = createResourceList(); @@ -89,10 +98,11 @@ public class BaseYangSwaggerGenerator { LOG.info("Modules found [{}]", modules.size()); for (final Module module : modules) { - final String revisionString = SIMPLE_DATE_FORMAT.format(module.getRevision()); + final String revisionString = module.getQNameModule().getRevision().map(Revision::toString).orElse(null); final Resource resource = new Resource(); LOG.debug("Working on [{},{}]...", module.getName(), revisionString); - final ApiDeclaration doc = getApiDeclaration(module.getName(), revisionString, uriInfo, schemaContext, context); + final ApiDeclaration doc = + getApiDeclaration(module.getName(), revisionString, uriInfo, schemaContext, context); if (doc != null) { resource.setPath(generatePath(uriInfo, module.getName(), revisionString)); @@ -107,47 +117,43 @@ public class BaseYangSwaggerGenerator { return resourceList; } - protected ResourceList createResourceList() { + public ResourceList createResourceList() { final ResourceList resourceList = new ResourceList(); resourceList.setApiVersion(API_VERSION); resourceList.setSwaggerVersion(SWAGGER_VERSION); return resourceList; } - protected String generatePath(final UriInfo uriInfo, final String name, final String revision) { + public String generatePath(final UriInfo uriInfo, final String name, final String revision) { final URI uri = uriInfo.getRequestUriBuilder().path(generateCacheKey(name, revision)).build(); return uri.toASCIIString(); } - public ApiDeclaration getApiDeclaration(final String moduleName, final String revision, final UriInfo uriInfo, final SchemaContext schemaContext, final String context) { - Date rev = null; + public ApiDeclaration getApiDeclaration(final String module, final String revision, final UriInfo uriInfo) { + final SchemaContext schemaContext = schemaService.getGlobalContext(); + Preconditions.checkState(schemaContext != null); + return getApiDeclaration(module, revision, uriInfo, schemaContext, ""); + } + + public ApiDeclaration getApiDeclaration(final String moduleName, final String revision, final UriInfo uriInfo, + final SchemaContext schemaContext, final String context) { + final Optional rev; try { - if (revision != null && !revision.equals("0000-00-00")) { - rev = SIMPLE_DATE_FORMAT.parse(revision); - } - } catch (final ParseException e) { + rev = Revision.ofNullable(revision); + } catch (final DateTimeParseException e) { throw new IllegalArgumentException(e); } - if (rev != null) { - final Calendar cal = new GregorianCalendar(); - - cal.setTime(rev); - - if (cal.get(Calendar.YEAR) < 1970) { - rev = null; - } - } - - final Module module = schemaContext.findModuleByName(moduleName, rev); + final Module module = schemaContext.findModule(moduleName, rev).orElse(null); Preconditions.checkArgument(module != null, "Could not find module by name,revision: " + moduleName + "," + revision); - return getApiDeclaration(module, rev, uriInfo, context, schemaContext); + return getApiDeclaration(module, uriInfo, context, schemaContext); } - public ApiDeclaration getApiDeclaration(final Module module, final Date revision, final UriInfo uriInfo, final String context, final SchemaContext schemaContext) { + public ApiDeclaration getApiDeclaration(final Module module, final UriInfo uriInfo, + final String context, final SchemaContext schemaContext) { final String basePath = createBasePathFromUriInfo(uriInfo); final ApiDeclaration doc = getSwaggerDocSpec(module, basePath, context, schemaContext); @@ -157,62 +163,68 @@ public class BaseYangSwaggerGenerator { return null; } - protected String createBasePathFromUriInfo(final UriInfo uriInfo) { + public String createBasePathFromUriInfo(final UriInfo uriInfo) { String portPart = ""; final int port = uriInfo.getBaseUri().getPort(); if (port != -1) { portPart = ":" + port; } - final String basePath = new StringBuilder(uriInfo.getBaseUri().getScheme()).append("://") - .append(uriInfo.getBaseUri().getHost()).append(portPart).append("/").append(RESTCONF_CONTEXT_ROOT) - .toString(); + final String basePath = + new StringBuilder(uriInfo.getBaseUri().getScheme()).append("://").append(uriInfo.getBaseUri().getHost()) + .append(portPart).toString(); return basePath; } - public ApiDeclaration getSwaggerDocSpec(final Module m, final String basePath, final String context, final SchemaContext schemaContext) { + public ApiDeclaration getSwaggerDocSpec(final Module module, final String basePath, final String context, + final SchemaContext schemaContext) { final ApiDeclaration doc = createApiDeclaration(basePath); final List apis = new ArrayList<>(); boolean hasAddRootPostLink = false; - final Collection dataSchemaNodes = m.getChildNodes(); + final Collection dataSchemaNodes = module.getChildNodes(); LOG.debug("child nodes size [{}]", dataSchemaNodes.size()); for (final DataSchemaNode node : dataSchemaNodes) { - if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) { + if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) { LOG.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node.getQName().getLocalName()); List pathParams = new ArrayList<>(); String resourcePath; /* - * Only when the node's config statement is true, such apis as GET/PUT/POST/DELETE config - * are added for this node. + * Only when the node's config statement is true, such apis as + * GET/PUT/POST/DELETE config are added for this node. */ - if (node.isConfiguration()) { // This node's config statement is true. - resourcePath = getDataStorePath("/config/", context); + if (node.isConfiguration()) { // This node's config statement is + // true. + resourcePath = getDataStorePath("config", context); /* - * When there are two or more top container or list nodes whose config statement is true in module, - * make sure that only one root post link is added for this module. + * When there are two or more top container or list nodes + * whose config statement is true in module, make sure that + * only one root post link is added for this module. */ if (!hasAddRootPostLink) { - LOG.debug("Has added root post link for module {}", m.getName()); - addRootPostLink(m, (DataNodeContainer) node, pathParams, resourcePath, apis); + LOG.debug("Has added root post link for module {}", module.getName()); + addRootPostLink(module, (DataNodeContainer) node, pathParams, resourcePath, "config", apis); + hasAddRootPostLink = true; } - addApis(node, apis, resourcePath, pathParams, schemaContext, true); + addApis(node, apis, resourcePath, pathParams, schemaContext, true, module.getName(), "config"); } - pathParams = new ArrayList<>(); - resourcePath = getDataStorePath("/operational/", context); - addApis(node, apis, resourcePath, pathParams, schemaContext, false); + resourcePath = getDataStorePath("operational", context); + + addApis(node, apis, resourcePath, pathParams, schemaContext, false, module.getName(), "operational"); } } - final Set rpcs = m.getRpcs(); + final Set rpcs = module.getRpcs(); for (final RpcDefinition rpcDefinition : rpcs) { - final String resourcePath = getDataStorePath("/operations/", context); + final String resourcePath; + resourcePath = getDataStorePath("operations", context); + addRpcs(rpcDefinition, apis, resourcePath, schemaContext); } @@ -220,15 +232,15 @@ public class BaseYangSwaggerGenerator { if (!apis.isEmpty()) { doc.setApis(apis); - JSONObject models = null; + ObjectNode models = null; try { - models = jsonConverter.convertToJsonSchema(m, schemaContext); + models = this.jsonConverter.convertToJsonSchema(module, schemaContext); doc.setModels(models); if (LOG.isDebugEnabled()) { - LOG.debug(mapper.writeValueAsString(doc)); + LOG.debug(this.mapper.writeValueAsString(doc)); } - } catch (IOException | JSONException e) { + } catch (IOException e) { LOG.error("Exception occured in ModelGenerator", e); } @@ -237,18 +249,18 @@ public class BaseYangSwaggerGenerator { return null; } - private void addRootPostLink(final Module module, final DataNodeContainer node, final List pathParams, - final String resourcePath, final List apis) { + private void addRootPostLink(final Module module, final DataNodeContainer node, + final List pathParams, final String resourcePath, final String dataStore, final List apis) { if (containsListOrContainer(module.getChildNodes())) { final Api apiForRootPostUri = new Api(); - apiForRootPostUri.setPath(resourcePath); + apiForRootPostUri.setPath(resourcePath.concat(getContent(dataStore))); apiForRootPostUri.setOperations(operationPost(module.getName() + MODULE_NAME_SUFFIX, - module.getDescription(), module, pathParams, true)); + module.getDescription().orElse(null), module, pathParams, true, "")); apis.add(apiForRootPostUri); } } - protected ApiDeclaration createApiDeclaration(final String basePath) { + public ApiDeclaration createApiDeclaration(final String basePath) { final ApiDeclaration doc = new ApiDeclaration(); doc.setApiVersion(API_VERSION); doc.setSwaggerVersion(SWAGGER_VERSION); @@ -257,44 +269,45 @@ public class BaseYangSwaggerGenerator { return doc; } - protected String getDataStorePath(final String dataStore, final String context) { - return dataStore + context; - } + public abstract String getDataStorePath(String dataStore, String context); - private String generateCacheKey(final String module, final String revision) { + private static String generateCacheKey(final String module, final String revision) { return module + "(" + revision + ")"; } - private void addApis(final DataSchemaNode node, final List apis, final String parentPath, final List parentPathParams, final SchemaContext schemaContext, - final boolean addConfigApi) { - + private void addApis(final DataSchemaNode node, final List apis, final String parentPath, + final List parentPathParams, final SchemaContext schemaContext, final boolean addConfigApi, + final String parentName, final String dataStore) { final Api api = new Api(); final List pathParams = new ArrayList<>(parentPathParams); - final String resourcePath = parentPath + createPath(node, pathParams, schemaContext) + "/"; + final String resourcePath = parentPath + "/" + createPath(node, pathParams, schemaContext); LOG.debug("Adding path: [{}]", resourcePath); - api.setPath(resourcePath); + api.setPath(resourcePath.concat(getContent(dataStore))); - Iterable childSchemaNodes = Collections.emptySet(); - if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) { + Iterable childSchemaNodes = Collections.emptySet(); + if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) { final DataNodeContainer dataNodeContainer = (DataNodeContainer) node; childSchemaNodes = dataNodeContainer.getChildNodes(); } - api.setOperations(operation(node, pathParams, addConfigApi, childSchemaNodes)); + api.setOperations(operation(node, pathParams, addConfigApi, childSchemaNodes, parentName)); apis.add(api); for (final DataSchemaNode childNode : childSchemaNodes) { if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) { // keep config and operation attributes separate. if (childNode.isConfiguration() == addConfigApi) { - addApis(childNode, apis, resourcePath, pathParams, schemaContext, addConfigApi); + final String newParent = parentName + "/" + node.getQName().getLocalName(); + addApis(childNode, apis, resourcePath, pathParams, schemaContext, addConfigApi, newParent, + dataStore); } } } - } - private boolean containsListOrContainer(final Iterable nodes) { + public abstract String getContent(String dataStore); + + private static boolean containsListOrContainer(final Iterable nodes) { for (final DataSchemaNode child : nodes) { if (child instanceof ListSchemaNode || child instanceof ContainerSchemaNode) { return true; @@ -303,56 +316,60 @@ public class BaseYangSwaggerGenerator { return false; } - private List operation(final DataSchemaNode node, final List pathParams, final boolean isConfig, final Iterable childSchemaNodes) { + private static List operation(final DataSchemaNode node, final List pathParams, + final boolean isConfig, final Iterable childSchemaNodes, final String parentName) { final List operations = new ArrayList<>(); final Get getBuilder = new Get(node, isConfig); operations.add(getBuilder.pathParams(pathParams).build()); if (isConfig) { - final Put putBuilder = new Put(node.getQName().getLocalName(), - node.getDescription()); + final Put putBuilder = new Put(node.getQName().getLocalName(), node.getDescription().orElse(null), + parentName); operations.add(putBuilder.pathParams(pathParams).build()); final Delete deleteBuilder = new Delete(node); operations.add(deleteBuilder.pathParams(pathParams).build()); if (containsListOrContainer(childSchemaNodes)) { - operations.addAll(operationPost(node.getQName().getLocalName(), node.getDescription(), - (DataNodeContainer) node, pathParams, isConfig)); + operations.addAll(operationPost(node.getQName().getLocalName(), node.getDescription().orElse(null), + (DataNodeContainer) node, pathParams, isConfig, parentName + "/")); } } return operations; } - private List operationPost(final String name, final String description, final DataNodeContainer dataNodeContainer, final List pathParams, final boolean isConfig) { + private static List operationPost(final String name, final String description, + final DataNodeContainer dataNodeContainer, final List pathParams, final boolean isConfig, + final String parentName) { final List operations = new ArrayList<>(); if (isConfig) { - final Post postBuilder = new Post(name, description, dataNodeContainer); + final Post postBuilder = new Post(name, parentName + name, description, dataNodeContainer); operations.add(postBuilder.pathParams(pathParams).build()); } return operations; } - private String createPath(final DataSchemaNode schemaNode, final List pathParams, final SchemaContext schemaContext) { - final ArrayList pathListParams = new ArrayList<>(); + protected abstract ListPathBuilder newListPathBuilder(); + + private String createPath(final DataSchemaNode schemaNode, final List pathParams, + final SchemaContext schemaContext) { final StringBuilder path = new StringBuilder(); final String localName = resolvePathArgumentsName(schemaNode, schemaContext); path.append(localName); - if ((schemaNode instanceof ListSchemaNode)) { + if (schemaNode instanceof ListSchemaNode) { final List listKeys = ((ListSchemaNode) schemaNode).getKeyDefinition(); for (final QName listKey : listKeys) { + final ListPathBuilder keyBuilder = newListPathBuilder(); final DataSchemaNode dataChildByName = ((DataNodeContainer) schemaNode).getDataChildByName(listKey); - pathListParams.add(((LeafSchemaNode) dataChildByName)); + final String pathParamIdentifier = keyBuilder.nextParamIdentifier(listKey.getLocalName()); - final String pathParamIdentifier = new StringBuilder("/{").append(listKey.getLocalName()).append("}") - .toString(); path.append(pathParamIdentifier); final Parameter pathParam = new Parameter(); pathParam.setName(listKey.getLocalName()); - pathParam.setDescription(dataChildByName.getDescription()); + pathParam.setDescription(dataChildByName.getDescription().orElse(null)); pathParam.setType("string"); pathParam.setParamType("path"); @@ -362,19 +379,20 @@ public class BaseYangSwaggerGenerator { return path.toString(); } - protected void addRpcs(final RpcDefinition rpcDefn, final List apis, final String parentPath, final SchemaContext schemaContext) { + protected void addRpcs(final RpcDefinition rpcDefn, final List apis, final String parentPath, + final SchemaContext schemaContext) { final Api rpc = new Api(); - final String resourcePath = parentPath + resolvePathArgumentsName(rpcDefn, schemaContext); + final String resourcePath = parentPath + "/" + resolvePathArgumentsName(rpcDefn, schemaContext); rpc.setPath(resourcePath); final Operation operationSpec = new Operation(); operationSpec.setMethod("POST"); - operationSpec.setNotes(rpcDefn.getDescription()); + operationSpec.setNotes(rpcDefn.getDescription().orElse(null)); operationSpec.setNickname(rpcDefn.getQName().getLocalName()); - if (rpcDefn.getOutput() != null) { + if (!rpcDefn.getOutput().getChildNodes().isEmpty()) { operationSpec.setType("(" + rpcDefn.getQName().getLocalName() + ")output" + OperationBuilder.TOP); } - if (rpcDefn.getInput() != null) { + if (!rpcDefn.getInput().getChildNodes().isEmpty()) { final Parameter payload = new Parameter(); payload.setParamType("body"); payload.setType("(" + rpcDefn.getQName().getLocalName() + ")input" + OperationBuilder.TOP); @@ -397,9 +415,7 @@ public class BaseYangSwaggerGenerator { final SortedSet sortedModules = new TreeSet<>((module1, module2) -> { int result = module1.getName().compareTo(module2.getName()); if (result == 0) { - final Date module1Revision = module1.getRevision() != null ? module1.getRevision() : new Date(0); - final Date module2Revision = module2.getRevision() != null ? module2.getRevision() : new Date(0); - result = module1Revision.compareTo(module2Revision); + result = Revision.compare(module1.getRevision(), module2.getRevision()); } if (result == 0) { result = module1.getNamespace().compareTo(module2.getNamespace()); @@ -414,4 +430,29 @@ public class BaseYangSwaggerGenerator { return sortedModules; } + protected abstract void appendPathKeyValue(StringBuilder builder, Object value); + + public String generateUrlPrefixFromInstanceID(final YangInstanceIdentifier key, final String moduleName) { + final StringBuilder builder = new StringBuilder(); + builder.append("/"); + if (moduleName != null) { + builder.append(moduleName).append(':'); + } + for (final PathArgument arg : key.getPathArguments()) { + final String name = arg.getNodeType().getLocalName(); + if (arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) { + final NodeIdentifierWithPredicates nodeId = (NodeIdentifierWithPredicates) arg; + for (final Entry entry : nodeId.getKeyValues().entrySet()) { + appendPathKeyValue(builder, entry.getValue()); + } + } else { + builder.append(name).append('/'); + } + } + return builder.toString(); + } + + protected interface ListPathBuilder { + String nextParamIdentifier(String key); + } }