*/
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.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.LeafSchemaNode;
+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;
-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";
- private static final String RESTCONF_DRAFT = "18";
+ 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<String, ApiDeclaration> MODULE_DOC_CACHE = new HashMap<>()
- private final ObjectMapper mapper = new ObjectMapper();
- private static boolean newDraft;
+ public static final String BASE_PATH = "/";
+ public static final String MODULE_NAME_SUFFIX = "_module";
- protected BaseYangSwaggerGenerator() {
- this.mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+ static {
+ MAPPER.configure(SerializationFeature.INDENT_OUTPUT, true);
}
- /**
- * Return list of modules converted to swagger compliant resource list.
- */
- public ResourceList getResourceListing(final UriInfo uriInfo, final SchemaContext schemaContext,
- final String context) {
+ protected BaseYangSwaggerGenerator(final Optional<DOMSchemaService> schemaService) {
+ this.schemaService = schemaService.orElse(null);
+ }
+
+ public DOMSchemaService getSchemaService() {
+ return schemaService;
+ }
- final ResourceList resourceList = createResourceList();
+ public SwaggerObject getAllModulesDoc(final UriInfo uriInfo, final DefinitionNames definitionNames,
+ final OAversion oaversion) {
+ final EffectiveModelContext schemaContext = schemaService.getGlobalContext();
+ Preconditions.checkState(schemaContext != null);
+ return getAllModulesDoc(uriInfo, Optional.empty(), schemaContext, Optional.empty(), "", definitionNames,
+ oaversion);
+ }
+
+ public SwaggerObject getAllModulesDoc(final UriInfo uriInfo, final Optional<Range<Integer>> range,
+ final EffectiveModelContext schemaContext, final Optional<String> 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 Set<Module> modules = getSortedModules(schemaContext);
+ 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 List<Resource> resources = new ArrayList<>(modules.size());
+ fillDoc(doc, range, schemaContext, context, deviceName, oaversion, definitionNames);
- LOG.info("Modules found [{}]", modules.size());
+ return doc;
+ }
+
+ public void fillDoc(final SwaggerObject doc, final Optional<Range<Integer>> range,
+ final EffectiveModelContext schemaContext, final String context, final Optional<String> deviceName,
+ final OAversion oaversion, final DefinitionNames definitionNames) {
+ final SortedSet<Module> modules = getSortedModules(schemaContext);
+ final Set<Module> 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<Module> filterByRange(final SortedSet<Module> modules, final Range<Integer> range) {
+ final int begin = range.lowerEndpoint();
+ final int end = range.upperEndpoint();
- return resourceList;
- }
+ Module firstModule = null;
- protected ResourceList createResourceList() {
- final ResourceList resourceList = new ResourceList();
- resourceList.setApiVersion(API_VERSION);
- resourceList.setSwaggerVersion(SWAGGER_VERSION);
- return resourceList;
+ final Iterator<Module> 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++;
+ }
+ }
+
+ if (iterator.hasNext()) {
+ return modules.subSet(firstModule, iterator.next());
+ } else {
+ return modules.tailSet(firstModule);
+ }
}
- protected 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 CommonApiObject getApiDeclaration(final String module, final String revision, final UriInfo uriInfo,
+ final OAversion oaversion) {
+ final EffectiveModelContext schemaContext = schemaService.getGlobalContext();
+ Preconditions.checkState(schemaContext != null);
+ 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<Revision> rev;
try {
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);
}
- protected 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).append("/").append(RESTCONF_CONTEXT_ROOT).toString();
- return basePath;
+ return uriInfo.getBaseUri().getHost() + portPart;
+ }
+
+ public String createSchemaFromUriInfo(final UriInfo uriInfo) {
+ return uriInfo.getBaseUri().getScheme();
}
- public ApiDeclaration getSwaggerDocSpec(final Module module, final String basePath, final String context,
- final SchemaContext schemaContext) {
- final ApiDeclaration doc = createApiDeclaration(basePath);
+ 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<String> 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<Api> apis = new ArrayList<>();
boolean hasAddRootPostLink = false;
- final Collection<DataSchemaNode> dataSchemaNodes = module.getChildNodes();
+ final Collection<? extends DataSchemaNode> 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<Parameter> 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<RpcDefinition> 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());
+ LOG.debug("Number of Paths found [{}]", paths.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);
- }
-
- return doc;
+ if (isForSingleModule) {
+ doc.setPaths(paths);
+ } else {
+ addFields(doc.getPaths(), paths.fields());
}
- return null;
+
+ return doc;
}
- private static void addRootPostLink(final Module module, final DataNodeContainer node,
- final List<Parameter> pathParams, final String resourcePath, final String dataStore, final List<Api> apis) {
+ private static void addRootPostLink(final Module module, final Optional<String> 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);
}
}
- protected 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;
}
- protected String getDataStorePath(final String dataStore, final String context) {
- if (newDraft) {
- if ("config".contains(dataStore) || "operational".contains(dataStore)) {
- return "/" + RESTCONF_DRAFT + "/data" + context;
- }
- return "/" + RESTCONF_DRAFT + "/operations" + context;
+ public static CommonApiObject getAppropriateDoc(final SwaggerObject swaggerObject, final OAversion oaversion) {
+ if (oaversion.equals(OAversion.V3_0)) {
+ return convertToOpenApi(swaggerObject);
}
+ return swaggerObject;
+ }
- return "/" + dataStore + context;
+ 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 static String generateCacheKey(final String module, final String revision) {
- return module + "(" + revision + ")";
+
+ private static List<Server> convertToServers(final List<String> schemes, final String host, final String basePath) {
+ return ImmutableList.of(new Server(schemes.get(0) + "://" + host + basePath));
}
- private void addApis(final DataSchemaNode node, final List<Api> apis, final String parentPath,
- final List<Parameter> parentPathParams, final SchemaContext schemaContext, final boolean addConfigApi,
- final String parentName, final String dataStore) {
- final Api api = new Api();
- final List<Parameter> pathParams = new ArrayList<>(parentPathParams);
+ protected abstract String getPathVersion();
- final String resourcePath = parentPath + "/" + createPath(node, pathParams, schemaContext);
+ public abstract String getResourcePath(String resourceType, String context);
+
+ public abstract String getResourcePathPart(String resourceType);
+
+ private void addPaths(final DataSchemaNode node, final Optional<String> 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<DataSchemaNode> childSchemaNodes = Collections.<DataSchemaNode>emptySet();
+ final ArrayNode pathParams = JsonUtil.copy(parentPathParams);
+ Iterable<? extends DataSchemaNode> 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);
- 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 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);
+ });
}
- }
- protected static String getContent(final String dataStore) {
- if (newDraft) {
- if ("operational".contains(dataStore)) {
- return "?content=nonconfig";
- } else if ("config".contains(dataStore)) {
- return "?content=config";
- } else {
- return "";
+ for (final DataSchemaNode childNode : childSchemaNodes) {
+ if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) {
+ 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);
}
- } else {
- return "";
}
}
- private static boolean containsListOrContainer(final Iterable<DataSchemaNode> nodes) {
+ private static boolean containsListOrContainer(final Iterable<? extends DataSchemaNode> nodes) {
for (final DataSchemaNode child : nodes) {
if (child instanceof ListSchemaNode || child instanceof ContainerSchemaNode) {
return true;
return false;
}
- private static List<Operation> operation(final DataSchemaNode node, final List<Parameter> pathParams,
- final boolean isConfig, final Iterable<DataSchemaNode> childSchemaNodes, final String parentName) {
- final List<Operation> operations = new ArrayList<>();
+ private static Map<String, ObjectNode> operations(final DataSchemaNode node, final String moduleName,
+ final Optional<String> deviceName, final ArrayNode pathParams, final boolean isConfig,
+ final String parentName, final DefinitionNames definitionNames, final OAversion oaversion) {
+ final Map<String, ObjectNode> operations = new HashMap<>();
+ final String discriminator = definitionNames.getDiscriminator(node);
+
+ final String nodeName = node.getQName().getLocalName();
+
+ final String defName = parentName + "_" + nodeName + TOP + discriminator;
+ final ObjectNode get = buildGet(node, moduleName, deviceName, pathParams, defName, isConfig, oaversion);
+ operations.put("get", get);
- 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().orElse(null),
- parentName);
- operations.add(putBuilder.pathParams(pathParams).build());
+ final ObjectNode put = buildPut(parentName, nodeName, discriminator, moduleName, deviceName,
+ node.getDescription().orElse(""), pathParams, oaversion);
+ operations.put("put", put);
- final Delete deleteBuilder = new Delete(node);
- operations.add(deleteBuilder.pathParams(pathParams).build());
+ final ObjectNode delete = buildDelete(node, moduleName, deviceName, pathParams, oaversion);
+ operations.put("delete", delete);
- if (containsListOrContainer(childSchemaNodes)) {
- operations.addAll(operationPost(node.getQName().getLocalName(), node.getDescription().orElse(null),
- (DataNodeContainer) node, pathParams, isConfig, parentName + "/"));
- }
+ operations.put("post", buildPost(parentName, nodeName, discriminator, moduleName, deviceName,
+ node.getDescription().orElse(""), pathParams, oaversion));
}
return operations;
}
- private static List<Operation> operationPost(final String name, final String description,
- final DataNodeContainer dataNodeContainer, final List<Parameter> pathParams, final boolean isConfig,
- final String parentName) {
- final List<Operation> operations = new ArrayList<>();
- if (isConfig) {
- final Post postBuilder = new Post(name, parentName + name, description, dataNodeContainer);
- operations.add(postBuilder.pathParams(pathParams).build());
- }
- return operations;
- }
+ protected abstract ListPathBuilder newListPathBuilder();
- private static String createPath(final DataSchemaNode schemaNode, final List<Parameter> pathParams,
- final SchemaContext schemaContext) {
- final ArrayList<LeafSchemaNode> pathListParams = new ArrayList<>();
+ 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<QName> listKeys = ((ListSchemaNode) schemaNode).getKeyDefinition();
- StringBuilder keyBuilder = null;
- if (newDraft) {
- keyBuilder = new StringBuilder("=");
- }
+ final ListPathBuilder keyBuilder = newListPathBuilder();
+ for (final QName listKey : ((ListSchemaNode) schemaNode).getKeyDefinition()) {
+ final String paramName = createUniquePathParamName(listKey.getLocalName(), pathParams);
+ final String pathParamIdentifier = keyBuilder.nextParamIdentifier(paramName);
- for (final QName listKey : listKeys) {
- final DataSchemaNode dataChildByName = ((DataNodeContainer) schemaNode).getDataChildByName(listKey);
- pathListParams.add((LeafSchemaNode) dataChildByName);
- final String pathParamIdentifier;
- if (newDraft) {
- pathParamIdentifier = keyBuilder.append("{").append(listKey.getLocalName()).append("}").toString();
- } else {
- pathParamIdentifier = "/{" + listKey.getLocalName() + "}";
- }
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);
- if (newDraft) {
- keyBuilder = new StringBuilder(",");
- }
}
}
return path.toString();
}
- protected void addRpcs(final RpcDefinition rpcDefn, final List<Api> 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<Module> getSortedModules(final SchemaContext schemaContext) {
+ public SortedSet<Module> getSortedModules(final EffectiveModelContext schemaContext) {
if (schemaContext == null) {
- return new TreeSet<>();
+ return Collections.emptySortedSet();
}
- final Set<Module> modules = schemaContext.getModules();
-
final SortedSet<Module> sortedModules = new TreeSet<>((module1, module2) -> {
int result = module1.getName().compareTo(module2.getName());
if (result == 0) {
}
return result;
});
- for (final Module m : modules) {
+ for (final Module m : schemaContext.getModules()) {
if (m != null) {
sortedModules.add(m);
}
return sortedModules;
}
- public void setDraft(final boolean draft) {
- BaseYangSwaggerGenerator.newDraft = draft;
+ private static void addOperations(final OperationDefinition operDef, final String moduleName,
+ final Optional<String> 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) {
+ 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 NodeIdentifierWithPredicates) {
+ final NodeIdentifierWithPredicates nodeId = (NodeIdentifierWithPredicates) arg;
+ for (final Entry<QName, Object> entry : nodeId.entrySet()) {
+ appendPathKeyValue(builder, entry.getValue());
+ }
+ } else {
+ builder.append(name).append('/');
+ }
+ }
+ return builder.toString();
+ }
+
+ protected interface ListPathBuilder {
+ String nextParamIdentifier(String key);
}
}