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=5881b2d12199f92aec455e528de724c7bd972ff0;hb=c5b2232060e8e14ae1213a8c017c50a5eff8681f;hp=af13ffe4dbccf37430138d8723a845331e017081;hpb=5defe99f336b51d4e96473aff6de4b9e6af8cb34;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 af13ffe4db..5881b2d121 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 @@ -7,50 +7,63 @@ */ package org.opendaylight.netconf.sal.rest.doc.impl; +import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.TOP; +import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.buildDelete; +import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.buildGet; +import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.buildPost; +import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.buildPostOperation; +import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.buildPut; +import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.getTypeParentNode; +import static org.opendaylight.netconf.sal.rest.doc.util.JsonUtil.addFields; import static org.opendaylight.netconf.sal.rest.doc.util.RestDocgenUtil.resolvePathArgumentsName; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Range; import java.io.IOException; -import java.net.URI; import java.time.format.DateTimeParseException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; 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.opendaylight.controller.sal.core.api.model.SchemaService; -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; -import org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.Post; -import org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.Put; -import org.opendaylight.netconf.sal.rest.doc.swagger.Api; -import org.opendaylight.netconf.sal.rest.doc.swagger.ApiDeclaration; -import org.opendaylight.netconf.sal.rest.doc.swagger.Operation; -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.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion; +import org.opendaylight.netconf.sal.rest.doc.swagger.CommonApiObject; +import org.opendaylight.netconf.sal.rest.doc.swagger.Components; +import org.opendaylight.netconf.sal.rest.doc.swagger.Info; +import org.opendaylight.netconf.sal.rest.doc.swagger.OpenApiObject; +import org.opendaylight.netconf.sal.rest.doc.swagger.Server; +import org.opendaylight.netconf.sal.rest.doc.swagger.SwaggerObject; +import org.opendaylight.netconf.sal.rest.doc.util.JsonUtil; 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.ActionNodeContainer; 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.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.OperationDefinition; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,85 +71,112 @@ 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"; + private static final String API_VERSION = "1.0.0"; + private static final String SWAGGER_VERSION = "2.0"; + private static final String OPEN_API_VERSION = "3.0.3"; + private static final ObjectMapper MAPPER = new ObjectMapper(); - static final String MODULE_NAME_SUFFIX = "_module"; - private final ModelGenerator jsonConverter = new ModelGenerator(); + private final DefinitionGenerator jsonConverter = new DefinitionGenerator(); + private final DOMSchemaService schemaService; - // private Map MODULE_DOC_CACHE = new HashMap<>() - private final ObjectMapper mapper = new ObjectMapper(); - private final SchemaService schemaService; + public static final String BASE_PATH = "/"; + public static final String MODULE_NAME_SUFFIX = "_module"; - protected BaseYangSwaggerGenerator(Optional schemaService) { + static { + MAPPER.configure(SerializationFeature.INDENT_OUTPUT, true); + } + + protected BaseYangSwaggerGenerator(final Optional schemaService) { this.schemaService = schemaService.orElse(null); - this.mapper.configure(SerializationFeature.INDENT_OUTPUT, true); } - public SchemaService getSchemaService() { + public DOMSchemaService getSchemaService() { return schemaService; } - public ResourceList getResourceListing(final UriInfo uriInfo) { - final SchemaContext schemaContext = schemaService.getGlobalContext(); + public SwaggerObject getAllModulesDoc(final UriInfo uriInfo, final DefinitionNames definitionNames, + final OAversion oaversion) { + final EffectiveModelContext schemaContext = schemaService.getGlobalContext(); Preconditions.checkState(schemaContext != null); - return getResourceListing(uriInfo, schemaContext, ""); + return getAllModulesDoc(uriInfo, Optional.empty(), schemaContext, Optional.empty(), "", definitionNames, + oaversion); } - /** - * Return list of modules converted to swagger compliant resource list. - */ - public ResourceList getResourceListing(final UriInfo uriInfo, final SchemaContext schemaContext, - final String context) { + public SwaggerObject getAllModulesDoc(final UriInfo uriInfo, final Optional> range, + final EffectiveModelContext schemaContext, final Optional deviceName, final String context, + final DefinitionNames definitionNames, final OAversion oaversion) { + final String schema = createSchemaFromUriInfo(uriInfo); + final String host = createHostFromUriInfo(uriInfo); + String name = "Controller"; + if (deviceName.isPresent()) { + name = deviceName.get(); + } - final ResourceList resourceList = createResourceList(); + final String title = name + " modules of RESTCONF"; + final SwaggerObject doc = createSwaggerObject(schema, host, BASE_PATH, title); + doc.setDefinitions(JsonNodeFactory.instance.objectNode()); + doc.setPaths(JsonNodeFactory.instance.objectNode()); - final Set modules = getSortedModules(schemaContext); + fillDoc(doc, range, schemaContext, context, deviceName, oaversion, definitionNames); - final List resources = new ArrayList<>(modules.size()); + return doc; + } - LOG.info("Modules found [{}]", modules.size()); + public void fillDoc(final SwaggerObject doc, final Optional> range, + final EffectiveModelContext schemaContext, final String context, final Optional deviceName, + final OAversion oaversion, final DefinitionNames definitionNames) { + final SortedSet modules = getSortedModules(schemaContext); + final Set filteredModules; + if (range.isPresent()) { + filteredModules = filterByRange(modules, range.get()); + } else { + filteredModules = modules; + } - for (final Module module : modules) { + for (final Module module : filteredModules) { 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); - if (doc != null) { - resource.setPath(generatePath(uriInfo, module.getName(), revisionString)); - resources.add(resource); - } else { - LOG.warn("Could not generate doc for {},{}", module.getName(), revisionString); - } + getSwaggerDocSpec(module, context, deviceName, schemaContext, oaversion, definitionNames, doc, false); } + } - resourceList.setApis(resources); + private static Set filterByRange(final SortedSet modules, final Range range) { + final int begin = range.lowerEndpoint(); + final int end = range.upperEndpoint(); - return resourceList; - } + Module firstModule = null; - public ResourceList createResourceList() { - final ResourceList resourceList = new ResourceList(); - resourceList.setApiVersion(API_VERSION); - resourceList.setSwaggerVersion(SWAGGER_VERSION); - return resourceList; - } + final Iterator iterator = modules.iterator(); + int counter = 0; + while (iterator.hasNext() && counter < end) { + final Module module = iterator.next(); + if (containsListOrContainer(module.getChildNodes()) || !module.getRpcs().isEmpty()) { + if (counter == begin) { + firstModule = module; + } + counter++; + } + } - 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(); + if (iterator.hasNext()) { + return modules.subSet(firstModule, iterator.next()); + } else { + return modules.tailSet(firstModule); + } } - public ApiDeclaration getApiDeclaration(final String module, final String revision, final UriInfo uriInfo) { - final SchemaContext schemaContext = schemaService.getGlobalContext(); + public CommonApiObject getApiDeclaration(final String module, final String revision, final UriInfo uriInfo, + final OAversion oaversion) { + final EffectiveModelContext schemaContext = schemaService.getGlobalContext(); Preconditions.checkState(schemaContext != null); - return getApiDeclaration(module, revision, uriInfo, schemaContext, ""); + final SwaggerObject doc = getApiDeclaration(module, revision, uriInfo, schemaContext, "", oaversion); + return getAppropriateDoc(doc, oaversion); } - public ApiDeclaration getApiDeclaration(final String moduleName, final String revision, final UriInfo uriInfo, - final SchemaContext schemaContext, final String context) { + public SwaggerObject getApiDeclaration(final String moduleName, final String revision, final UriInfo uriInfo, + final EffectiveModelContext schemaContext, final String context, final OAversion oaversion) { final Optional rev; try { @@ -149,165 +189,222 @@ public abstract class BaseYangSwaggerGenerator { Preconditions.checkArgument(module != null, "Could not find module by name,revision: " + moduleName + "," + revision); - return getApiDeclaration(module, uriInfo, context, schemaContext); + return getApiDeclaration(module, uriInfo, context, schemaContext, oaversion); } - public ApiDeclaration getApiDeclaration(final Module module, final UriInfo uriInfo, - final String context, final SchemaContext schemaContext) { - final String basePath = createBasePathFromUriInfo(uriInfo); + public SwaggerObject getApiDeclaration(final Module module, final UriInfo uriInfo, final String context, + final EffectiveModelContext schemaContext, final OAversion oaversion) { + final String schema = createSchemaFromUriInfo(uriInfo); + final String host = createHostFromUriInfo(uriInfo); - final ApiDeclaration doc = getSwaggerDocSpec(module, basePath, context, schemaContext); - if (doc != null) { - return doc; - } - return null; + return getSwaggerDocSpec(module, schema, host, BASE_PATH, context, schemaContext, oaversion); } - public String createBasePathFromUriInfo(final UriInfo uriInfo) { + public String createHostFromUriInfo(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).toString(); - return basePath; + return uriInfo.getBaseUri().getHost() + portPart; } - public ApiDeclaration getSwaggerDocSpec(final Module module, final String basePath, final String context, - final SchemaContext schemaContext) { - final ApiDeclaration doc = createApiDeclaration(basePath); + public String createSchemaFromUriInfo(final UriInfo uriInfo) { + return uriInfo.getBaseUri().getScheme(); + } + + public SwaggerObject getSwaggerDocSpec(final Module module, final String schema, final String host, + final String basePath, final String context, final EffectiveModelContext schemaContext, + final OAversion oaversion) { + final SwaggerObject doc = createSwaggerObject(schema, host, basePath, module.getName()); + final DefinitionNames definitionNames = new DefinitionNames(); + return getSwaggerDocSpec(module, context, Optional.empty(), schemaContext, oaversion, definitionNames, doc, + true); + } + + public SwaggerObject getSwaggerDocSpec(final Module module, final String context, final Optional deviceName, + final EffectiveModelContext schemaContext, final OAversion oaversion, final DefinitionNames definitionNames, + final SwaggerObject doc, final boolean isForSingleModule) { + final ObjectNode definitions; + + try { + if (isForSingleModule) { + definitions = jsonConverter.convertToJsonSchema(module, schemaContext, definitionNames, oaversion, + true); + doc.setDefinitions(definitions); + } else { + definitions = jsonConverter.convertToJsonSchema(module, schemaContext, definitionNames, oaversion, + false); + addFields(doc.getDefinitions(), definitions.fields()); + } + if (LOG.isDebugEnabled()) { + LOG.debug("Document: {}", MAPPER.writeValueAsString(doc)); + } + } catch (final IOException e) { + LOG.error("Exception occured in DefinitionGenerator", e); + } + + final ObjectNode paths = JsonNodeFactory.instance.objectNode(); + final String moduleName = module.getName(); - final List apis = new ArrayList<>(); boolean hasAddRootPostLink = false; - final Collection dataSchemaNodes = module.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) { LOG.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node.getQName().getLocalName()); - List pathParams = new ArrayList<>(); + final String localName = module.getName() + ":" + node.getQName().getLocalName(); + ArrayNode pathParams = JsonNodeFactory.instance.arrayNode(); String resourcePath; - /* - * 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); + // true. + resourcePath = getResourcePath("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. */ - if (!hasAddRootPostLink) { + if (isForSingleModule && !hasAddRootPostLink) { LOG.debug("Has added root post link for module {}", module.getName()); - addRootPostLink(module, (DataNodeContainer) node, pathParams, resourcePath, "config", apis); + addRootPostLink(module, deviceName, pathParams, resourcePath, paths, oaversion); hasAddRootPostLink = true; } - addApis(node, apis, resourcePath, pathParams, schemaContext, true, module.getName(), "config"); + final String resolvedPath = resourcePath + "/" + createPath(node, pathParams, oaversion, localName); + addPaths(node, deviceName, moduleName, paths, pathParams, schemaContext, true, module.getName(), + definitionNames, oaversion, resolvedPath); } - pathParams = new ArrayList<>(); - resourcePath = getDataStorePath("operational", context); + pathParams = JsonNodeFactory.instance.arrayNode(); + resourcePath = getResourcePath("operational", context); - addApis(node, apis, resourcePath, pathParams, schemaContext, false, module.getName(), "operational"); + if (!node.isConfiguration()) { + final String resolvedPath = resourcePath + "/" + createPath(node, pathParams, oaversion, localName); + addPaths(node, deviceName, moduleName, paths, pathParams, schemaContext, false, moduleName, + definitionNames, oaversion, resolvedPath); + } } } - final Set rpcs = module.getRpcs(); - for (final RpcDefinition rpcDefinition : rpcs) { - final String resourcePath; - resourcePath = getDataStorePath("operations", context); - - addRpcs(rpcDefinition, apis, resourcePath, schemaContext); + for (final RpcDefinition rpcDefinition : module.getRpcs()) { + final String resolvedPath = getResourcePath("operations", context) + "/" + moduleName + ":" + + rpcDefinition.getQName().getLocalName(); + addOperations(rpcDefinition, moduleName, deviceName, paths, module.getName(), definitionNames, oaversion, + resolvedPath); } - LOG.debug("Number of APIs found [{}]", apis.size()); - - if (!apis.isEmpty()) { - doc.setApis(apis); - ObjectNode models = null; - - try { - models = this.jsonConverter.convertToJsonSchema(module, schemaContext); - doc.setModels(models); - if (LOG.isDebugEnabled()) { - LOG.debug(this.mapper.writeValueAsString(doc)); - } - } catch (IOException e) { - LOG.error("Exception occured in ModelGenerator", e); - } + LOG.debug("Number of Paths found [{}]", paths.size()); - return doc; + if (isForSingleModule) { + doc.setPaths(paths); + } else { + addFields(doc.getPaths(), paths.fields()); } - return null; + + return doc; } - private void addRootPostLink(final Module module, final DataNodeContainer node, - final List pathParams, final String resourcePath, final String dataStore, final List apis) { + private static void addRootPostLink(final Module module, final Optional deviceName, + final ArrayNode pathParams, final String resourcePath, final ObjectNode paths, final OAversion oaversion) { if (containsListOrContainer(module.getChildNodes())) { - final Api apiForRootPostUri = new Api(); - apiForRootPostUri.setPath(resourcePath.concat(getContent(dataStore))); - apiForRootPostUri.setOperations(operationPost(module.getName() + MODULE_NAME_SUFFIX, - module.getDescription().orElse(null), module, pathParams, true, "")); - apis.add(apiForRootPostUri); + final ObjectNode post = JsonNodeFactory.instance.objectNode(); + final String moduleName = module.getName(); + final String name = moduleName + MODULE_NAME_SUFFIX; + post.set("post", buildPost("", name, "", moduleName, deviceName, + module.getDescription().orElse(""), pathParams, oaversion)); + paths.set(resourcePath, post); } } - public ApiDeclaration createApiDeclaration(final String basePath) { - final ApiDeclaration doc = new ApiDeclaration(); - doc.setApiVersion(API_VERSION); - doc.setSwaggerVersion(SWAGGER_VERSION); + public SwaggerObject createSwaggerObject(final String schema, final String host, final String basePath, + final String title) { + final SwaggerObject doc = new SwaggerObject(); + doc.setSwagger(SWAGGER_VERSION); + final Info info = new Info(); + info.setTitle(title); + info.setVersion(API_VERSION); + doc.setInfo(info); + doc.setSchemes(ImmutableList.of(schema)); + doc.setHost(host); doc.setBasePath(basePath); - doc.setProduces(Arrays.asList("application/json", "application/xml")); + doc.setProduces(Arrays.asList("application/xml", "application/json")); return doc; } - public abstract String getDataStorePath(String dataStore, String context); + public static CommonApiObject getAppropriateDoc(final SwaggerObject swaggerObject, final OAversion oaversion) { + if (oaversion.equals(OAversion.V3_0)) { + return convertToOpenApi(swaggerObject); + } + return swaggerObject; + } - private static String generateCacheKey(final String module, final String revision) { - return module + "(" + revision + ")"; + private static OpenApiObject convertToOpenApi(final SwaggerObject swaggerObject) { + final OpenApiObject doc = new OpenApiObject(); + doc.setOpenapi(OPEN_API_VERSION); + doc.setInfo(swaggerObject.getInfo()); + doc.setServers(convertToServers(swaggerObject.getSchemes(), swaggerObject.getHost(), + swaggerObject.getBasePath())); + doc.setPaths(swaggerObject.getPaths()); + doc.setComponents(new Components(swaggerObject.getDefinitions())); + return doc; } - 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); + private static List convertToServers(final List schemes, final String host, final String basePath) { + return ImmutableList.of(new Server(schemes.get(0) + "://" + host + basePath)); + } + + protected abstract String getPathVersion(); + + public abstract String getResourcePath(String resourceType, String context); + + public abstract String getResourcePathPart(String resourceType); + + private void addPaths(final DataSchemaNode node, final Optional deviceName, final String moduleName, + final ObjectNode paths, final ArrayNode parentPathParams, final EffectiveModelContext schemaContext, + final boolean isConfig, final String parentName, final DefinitionNames definitionNames, + final OAversion oaversion, final String resourcePath) { LOG.debug("Adding path: [{}]", resourcePath); - api.setPath(resourcePath.concat(getContent(dataStore))); - Iterable childSchemaNodes = Collections.emptySet(); + final ArrayNode pathParams = JsonUtil.copy(parentPathParams); + 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, parentName)); - apis.add(api); + + final ObjectNode path = JsonNodeFactory.instance.objectNode(); + path.setAll(operations(node, moduleName, deviceName, pathParams, isConfig, parentName, definitionNames, + oaversion)); + paths.set(resourcePath, path); + + if (node instanceof ActionNodeContainer) { + ((ActionNodeContainer) node).getActions().forEach(actionDef -> { + final String resolvedPath = "rests/operations" + resourcePath.substring(11) + + "/" + resolvePathArgumentsName(actionDef.getQName(), node.getQName(), schemaContext); + addOperations(actionDef, moduleName, deviceName, paths, parentName, definitionNames, oaversion, + resolvedPath); + }); + } for (final DataSchemaNode childNode : childSchemaNodes) { if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) { - // keep config and operation attributes separate. - if (childNode.isConfiguration() == addConfigApi) { - final String newParent = parentName + "/" + node.getQName().getLocalName(); - addApis(childNode, apis, resourcePath, pathParams, schemaContext, addConfigApi, newParent, - dataStore); - } + final String newParent = parentName + "_" + node.getQName().getLocalName(); + final String localName = resolvePathArgumentsName(childNode.getQName(), node.getQName(), schemaContext); + final String newResourcePath = resourcePath + "/" + createPath(childNode, pathParams, oaversion, + localName); + final boolean newIsConfig = isConfig && childNode.isConfiguration(); + addPaths(childNode, deviceName, moduleName, paths, pathParams, schemaContext, + newIsConfig, newParent, definitionNames, oaversion, newResourcePath); } } } - public abstract String getContent(String dataStore); - - private static boolean containsListOrContainer(final Iterable nodes) { + private static boolean containsListOrContainer(final Iterable nodes) { for (final DataSchemaNode child : nodes) { if (child instanceof ListSchemaNode || child instanceof ContainerSchemaNode) { return true; @@ -316,62 +413,59 @@ public abstract class BaseYangSwaggerGenerator { return false; } - 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()); + private static Map operations(final DataSchemaNode node, final String moduleName, + final Optional deviceName, final ArrayNode pathParams, final boolean isConfig, + final String parentName, final DefinitionNames definitionNames, final OAversion oaversion) { + final Map operations = new HashMap<>(); + final String discriminator = definitionNames.getDiscriminator(node); - if (isConfig) { - final Put putBuilder = new Put(node.getQName().getLocalName(), node.getDescription().orElse(null), - parentName); - operations.add(putBuilder.pathParams(pathParams).build()); + final String nodeName = node.getQName().getLocalName(); - final Delete deleteBuilder = new Delete(node); - operations.add(deleteBuilder.pathParams(pathParams).build()); + final String defName = parentName + "_" + nodeName + TOP + discriminator; + final ObjectNode get = buildGet(node, moduleName, deviceName, pathParams, defName, isConfig, oaversion); + operations.put("get", get); - if (containsListOrContainer(childSchemaNodes)) { - operations.addAll(operationPost(node.getQName().getLocalName(), node.getDescription().orElse(null), - (DataNodeContainer) node, pathParams, isConfig, parentName + "/")); - } - } - return operations; - } - 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, parentName + name, description, dataNodeContainer); - operations.add(postBuilder.pathParams(pathParams).build()); + final ObjectNode put = buildPut(parentName, nodeName, discriminator, moduleName, deviceName, + node.getDescription().orElse(""), pathParams, oaversion); + operations.put("put", put); + + final ObjectNode delete = buildDelete(node, moduleName, deviceName, pathParams, oaversion); + operations.put("delete", delete); + + operations.put("post", buildPost(parentName, nodeName, discriminator, moduleName, deviceName, + node.getDescription().orElse(""), pathParams, oaversion)); } return operations; } protected abstract ListPathBuilder newListPathBuilder(); - private String createPath(final DataSchemaNode schemaNode, final List pathParams, - final SchemaContext schemaContext) { + private String createPath(final DataSchemaNode schemaNode, final ArrayNode pathParams, + final OAversion oaversion, final String localName) { final StringBuilder path = new StringBuilder(); - final String localName = resolvePathArgumentsName(schemaNode, schemaContext); path.append(localName); 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); - final String pathParamIdentifier = keyBuilder.nextParamIdentifier(listKey.getLocalName()); + final ListPathBuilder keyBuilder = newListPathBuilder(); + for (final QName listKey : ((ListSchemaNode) schemaNode).getKeyDefinition()) { + final String paramName = createUniquePathParamName(listKey.getLocalName(), pathParams); + final String pathParamIdentifier = keyBuilder.nextParamIdentifier(paramName); path.append(pathParamIdentifier); - final Parameter pathParam = new Parameter(); - pathParam.setName(listKey.getLocalName()); - pathParam.setDescription(dataChildByName.getDescription().orElse(null)); - pathParam.setType("string"); - pathParam.setParamType("path"); + final ObjectNode pathParam = JsonNodeFactory.instance.objectNode(); + pathParam.put("name", paramName); + + ((DataNodeContainer) schemaNode).findDataChildByName(listKey).flatMap(DataSchemaNode::getDescription) + .ifPresent(desc -> pathParam.put("description", desc)); + + final ObjectNode typeParent = getTypeParentNode(pathParam, oaversion); + + typeParent.put("type", "string"); + pathParam.put("in", "path"); + pathParam.put("required", true); pathParams.add(pathParam); } @@ -379,39 +473,35 @@ public abstract class BaseYangSwaggerGenerator { return path.toString(); } - 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); - rpc.setPath(resourcePath); - - final Operation operationSpec = new Operation(); - operationSpec.setMethod("POST"); - operationSpec.setNotes(rpcDefn.getDescription().orElse(null)); - operationSpec.setNickname(rpcDefn.getQName().getLocalName()); - if (!rpcDefn.getOutput().getChildNodes().isEmpty()) { - operationSpec.setType("(" + rpcDefn.getQName().getLocalName() + ")output" + OperationBuilder.TOP); - } - if (!rpcDefn.getInput().getChildNodes().isEmpty()) { - final Parameter payload = new Parameter(); - payload.setParamType("body"); - payload.setType("(" + rpcDefn.getQName().getLocalName() + ")input" + OperationBuilder.TOP); - operationSpec.setParameters(Collections.singletonList(payload)); - operationSpec.setConsumes(OperationBuilder.CONSUMES_PUT_POST); + private String createUniquePathParamName(final String clearName, final ArrayNode pathParams) { + for (final JsonNode pathParam : pathParams) { + if (isNamePicked(clearName, pathParam)) { + return createUniquePathParamName(clearName, pathParams, 1); + } } + return clearName; + } - rpc.setOperations(Arrays.asList(operationSpec)); + private String createUniquePathParamName(final String clearName, final ArrayNode pathParams, + final int discriminator) { + final String newName = clearName + discriminator; + for (final JsonNode pathParam : pathParams) { + if (isNamePicked(newName, pathParam)) { + return createUniquePathParamName(clearName, pathParams, discriminator + 1); + } + } + return newName; + } - apis.add(rpc); + private static boolean isNamePicked(final String name, final JsonNode pathParam) { + return name.equals(pathParam.get("name").asText()); } - protected SortedSet getSortedModules(final SchemaContext schemaContext) { + public SortedSet getSortedModules(final EffectiveModelContext schemaContext) { if (schemaContext == null) { - return new TreeSet<>(); + return Collections.emptySortedSet(); } - final Set modules = schemaContext.getModules(); - final SortedSet sortedModules = new TreeSet<>((module1, module2) -> { int result = module1.getName().compareTo(module2.getName()); if (result == 0) { @@ -422,7 +512,7 @@ public abstract class BaseYangSwaggerGenerator { } return result; }); - for (final Module m : modules) { + for (final Module m : schemaContext.getModules()) { if (m != null) { sortedModules.add(m); } @@ -430,6 +520,15 @@ public abstract class BaseYangSwaggerGenerator { return sortedModules; } + private static void addOperations(final OperationDefinition operDef, final String moduleName, + final Optional deviceName, final ObjectNode paths, final String parentName, + final DefinitionNames definitionNames, final OAversion oaversion, final String resourcePath) { + final ObjectNode operations = JsonNodeFactory.instance.objectNode(); + operations.set("post", buildPostOperation(operDef, moduleName, deviceName, parentName, definitionNames, + oaversion)); + paths.set(resourcePath, operations); + } + protected abstract void appendPathKeyValue(StringBuilder builder, Object value); public String generateUrlPrefixFromInstanceID(final YangInstanceIdentifier key, final String moduleName) { @@ -440,9 +539,9 @@ public abstract class BaseYangSwaggerGenerator { } for (final PathArgument arg : key.getPathArguments()) { final String name = arg.getNodeType().getLocalName(); - if (arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) { + if (arg instanceof NodeIdentifierWithPredicates) { final NodeIdentifierWithPredicates nodeId = (NodeIdentifierWithPredicates) arg; - for (final Entry entry : nodeId.getKeyValues().entrySet()) { + for (final Entry entry : nodeId.entrySet()) { appendPathKeyValue(builder, entry.getValue()); } } else {