/*
- * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
*/
package org.opendaylight.netconf.sal.rest.doc.mountpoints;
-import com.google.common.base.Optional;
-import java.util.Collections;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.DEFAULT_PAGESIZE;
+import static org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator.BASE_PATH;
+import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.DESCRIPTION_KEY;
+import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.RESPONSES_KEY;
+import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.SUMMARY_KEY;
+import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.SUMMARY_SEPARATOR;
+import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.TAGS_KEY;
+import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.buildTagsValue;
+import static org.opendaylight.netconf.sal.rest.doc.util.JsonUtil.addFields;
+
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Range;
import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Optional;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
-import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
-import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
-import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.mdsal.dom.api.DOMMountPointListener;
+import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
import org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator;
-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.Resource;
-import org.opendaylight.netconf.sal.rest.doc.swagger.ResourceList;
-import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.netconf.sal.rest.doc.impl.DefinitionNames;
+import org.opendaylight.netconf.sal.rest.doc.swagger.CommonApiObject;
+import org.opendaylight.netconf.sal.rest.doc.swagger.SwaggerObject;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
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.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MountPointSwagger implements DOMMountPointListener, AutoCloseable {
-public class MountPointSwagger extends BaseYangSwaggerGenerator implements MountProvisionListener {
+ private static final Logger LOG = LoggerFactory.getLogger(MountPointSwagger.class);
private static final String DATASTORES_REVISION = "-";
private static final String DATASTORES_LABEL = "Datastores";
- private static final String RESTCONF_DRAFT = "18";
- private DOMMountPointService mountService;
+ private final DOMSchemaService globalSchema;
+ private final DOMMountPointService mountService;
+ private final BaseYangSwaggerGenerator swaggerGenerator;
private final Map<YangInstanceIdentifier, Long> instanceIdToLongId =
new TreeMap<>((o1, o2) -> o1.toString().compareToIgnoreCase(o2.toString()));
private final Map<Long, YangInstanceIdentifier> longIdToInstanceId = new HashMap<>();
+
private final Object lock = new Object();
private final AtomicLong idKey = new AtomicLong(0);
- private static final AtomicReference<MountPointSwagger> selfRef = new AtomicReference<>();
- private SchemaService globalSchema;
- private static boolean newDraft;
+ private ListenerRegistration<DOMMountPointListener> registration;
+
+ public MountPointSwagger(final DOMSchemaService globalSchema, final DOMMountPointService mountService,
+ final BaseYangSwaggerGenerator swaggerGenerator) {
+ this.globalSchema = requireNonNull(globalSchema);
+ this.mountService = requireNonNull(mountService);
+ this.swaggerGenerator = requireNonNull(swaggerGenerator);
+ }
+
+ public void init() {
+ registration = mountService.registerProvisionListener(this);
+ }
+
+ @Override
+ public void close() {
+ if (registration != null) {
+ registration.close();
+ }
+ }
public Map<String, Long> getInstanceIdentifiers() {
final Map<String, Long> urlToId = new HashMap<>();
- synchronized (this.lock) {
- final SchemaContext context = this.globalSchema.getGlobalContext();
- for (final Entry<YangInstanceIdentifier, Long> entry : this.instanceIdToLongId.entrySet()) {
+ synchronized (lock) {
+ final SchemaContext context = globalSchema.getGlobalContext();
+ for (final Entry<YangInstanceIdentifier, Long> entry : instanceIdToLongId.entrySet()) {
final String modName = findModuleName(entry.getKey(), context);
- urlToId.put(generateUrlPrefixFromInstanceID(entry.getKey(), modName), entry.getValue());
+ urlToId.put(swaggerGenerator.generateUrlPrefixFromInstanceID(entry.getKey(), modName),
+ entry.getValue());
}
}
return urlToId;
}
- public void setGlobalSchema(final SchemaService globalSchema) {
- this.globalSchema = globalSchema;
- }
-
- private String findModuleName(final YangInstanceIdentifier id, final SchemaContext context) {
+ private static String findModuleName(final YangInstanceIdentifier id, final SchemaContext context) {
final PathArgument rootQName = id.getPathArguments().iterator().next();
for (final Module mod : context.getModules()) {
- if (mod.getDataChildByName(rootQName.getNodeType()) != null) {
+ if (mod.findDataChildByName(rootQName.getNodeType()).isPresent()) {
return mod.getName();
}
}
return null;
}
- private 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<QName, Object> entry : nodeId.getKeyValues().entrySet()) {
- if (newDraft) {
- builder.deleteCharAt(builder.length() - 1).append("=").append(entry.getValue()).append('/');
- } else {
- builder.append(entry.getValue()).append('/');
- }
- }
- } else {
- builder.append(name).append('/');
- }
- }
- return builder.toString();
- }
-
private String getYangMountUrl(final YangInstanceIdentifier key) {
- final String modName = findModuleName(key, this.globalSchema.getGlobalContext());
- return generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount";
- }
-
- public ResourceList getResourceList(final UriInfo uriInfo, final Long id) {
- final YangInstanceIdentifier iid = getInstanceId(id);
- if (iid == null) {
- return null; // indicating not found.
- }
- final SchemaContext context = getSchemaContext(iid);
- if (context == null) {
- return createResourceList();
- }
- final List<Resource> resources = new LinkedList<>();
- final Resource dataStores = new Resource();
- dataStores.setDescription("Provides methods for accessing the data stores.");
- dataStores.setPath(generatePath(uriInfo, DATASTORES_LABEL, DATASTORES_REVISION));
- resources.add(dataStores);
- final String urlPrefix = getYangMountUrl(iid);
- final ResourceList list = super.getResourceListing(uriInfo, context, urlPrefix);
- resources.addAll(list.getApis());
- list.setApis(resources);
- return list;
+ final String modName = findModuleName(key, globalSchema.getGlobalContext());
+ return swaggerGenerator.generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount";
}
private YangInstanceIdentifier getInstanceId(final Long id) {
final YangInstanceIdentifier instanceId;
- synchronized (this.lock) {
- instanceId = this.longIdToInstanceId.get(id);
+ synchronized (lock) {
+ instanceId = longIdToInstanceId.get(id);
}
return instanceId;
}
- private SchemaContext getSchemaContext(final YangInstanceIdentifier id) {
-
+ private EffectiveModelContext getSchemaContext(final YangInstanceIdentifier id) {
if (id == null) {
return null;
}
- final Optional<DOMMountPoint> mountPoint = this.mountService.getMountPoint(id);
- if (!mountPoint.isPresent()) {
- return null;
- }
+ checkState(mountService != null);
+ return mountService.getMountPoint(id)
+ .flatMap(mountPoint -> mountPoint.getService(DOMSchemaService.class))
+ .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
+ .orElse(null);
+ }
+
+ public CommonApiObject getMountPointApi(final UriInfo uriInfo, final Long id, final String module,
+ final String revision, final OAversion oaversion) {
+ final YangInstanceIdentifier iid = getInstanceId(id);
+ final EffectiveModelContext context = getSchemaContext(iid);
+ final String urlPrefix = getYangMountUrl(iid);
+ final String deviceName = extractDeviceName(iid);
- final SchemaContext context = mountPoint.get().getSchemaContext();
if (context == null) {
return null;
}
- return context;
+
+ if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
+ return generateDataStoreApiDoc(uriInfo, urlPrefix, deviceName);
+ }
+ final SwaggerObject swaggerObject = swaggerGenerator.getApiDeclaration(module, revision, uriInfo, context,
+ urlPrefix, oaversion);
+ return BaseYangSwaggerGenerator.getAppropriateDoc(swaggerObject, oaversion);
}
- public ApiDeclaration getMountPointApi(final UriInfo uriInfo, final Long id, final String module,
- final String revision) {
+ public CommonApiObject getMountPointApi(final UriInfo uriInfo, final Long id, final Optional<Integer> pageNum,
+ final OAversion oaversion) {
final YangInstanceIdentifier iid = getInstanceId(id);
- final SchemaContext context = getSchemaContext(iid);
+ final EffectiveModelContext context = getSchemaContext(iid);
final String urlPrefix = getYangMountUrl(iid);
+ final String deviceName = extractDeviceName(iid);
+
if (context == null) {
return null;
}
+ final DefinitionNames definitionNames = new DefinitionNames();
- if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
- return generateDataStoreApiDoc(uriInfo, urlPrefix);
+ boolean includeDataStore = true;
+ Optional<Range<Integer>> range = Optional.empty();
+
+ if (pageNum.isPresent()) {
+ final int pageNumValue = pageNum.get();
+ final int end = DEFAULT_PAGESIZE * pageNumValue - 1;
+ int start = end - DEFAULT_PAGESIZE;
+ if (pageNumValue == 1) {
+ start++;
+ } else {
+ includeDataStore = false;
+ }
+ range = Optional.of(Range.closed(start, end));
}
- return super.getApiDeclaration(module, revision, uriInfo, context, urlPrefix);
- }
- private ApiDeclaration generateDataStoreApiDoc(final UriInfo uriInfo, final String context) {
- final List<Api> apis = new LinkedList<>();
- apis.add(createGetApi("config", "Queries the config (startup) datastore on the mounted hosted.", context));
- apis.add(createGetApi("operational", "Queries the operational (running) datastore on the mounted hosted.",
- context));
- apis.add(createGetApi("operations", "Queries the available operations (RPC calls) on the mounted hosted.",
- context));
+ final SwaggerObject doc;
- final ApiDeclaration declaration = super.createApiDeclaration(createBasePathFromUriInfo(uriInfo));
- declaration.setApis(apis);
- return declaration;
+ final SwaggerObject swaggerObject = swaggerGenerator.getAllModulesDoc(uriInfo, range, context,
+ Optional.of(deviceName), urlPrefix, definitionNames, oaversion);
+
+ if (includeDataStore) {
+ doc = generateDataStoreApiDoc(uriInfo, urlPrefix, deviceName);
+ addFields(doc.getPaths() ,swaggerObject.getPaths().fields());
+ addFields(doc.getDefinitions() ,swaggerObject.getDefinitions().fields());
+ doc.getInfo().setTitle(swaggerObject.getInfo().getTitle());
+ } else {
+ doc = swaggerObject;
+ }
+ return BaseYangSwaggerGenerator.getAppropriateDoc(doc, oaversion);
}
- private Api createGetApi(final String datastore, final String note, final String context) {
- final Operation getConfig = new Operation();
- getConfig.setMethod("GET");
- getConfig.setNickname("GET " + datastore);
- getConfig.setNotes(note);
+ private static String extractDeviceName(final YangInstanceIdentifier iid) {
+ return ((YangInstanceIdentifier.NodeIdentifierWithPredicates.Singleton)iid.getLastPathArgument())
+ .values().getElement().toString();
+ }
- final Api api = new Api();
- api.setPath(getDataStorePath(datastore, context).concat(getContent(datastore)));
- api.setOperations(Collections.singletonList(getConfig));
+ private SwaggerObject generateDataStoreApiDoc(final UriInfo uriInfo, final String context,
+ final String deviceName) {
+ final SwaggerObject declaration = swaggerGenerator.createSwaggerObject(
+ swaggerGenerator.createSchemaFromUriInfo(uriInfo),
+ swaggerGenerator.createHostFromUriInfo(uriInfo),
+ BASE_PATH,
+ context);
- return api;
+ final ObjectNode pathsObject = JsonNodeFactory.instance.objectNode();
+ createGetPathItem("config", "Queries the config (startup) datastore on the mounted hosted.",
+ context, deviceName, pathsObject);
+ createGetPathItem("operational", "Queries the operational (running) datastore on the mounted hosted.",
+ context, deviceName, pathsObject);
+ createGetPathItem("operations", "Queries the available operations (RPC calls) on the mounted hosted.",
+ context, deviceName, pathsObject);
+
+ declaration.setPaths(pathsObject);
+ declaration.setDefinitions(JsonNodeFactory.instance.objectNode());
+
+ return declaration;
}
- public void setMountService(final DOMMountPointService mountService) {
- this.mountService = mountService;
+ private void createGetPathItem(final String resourceType, final String description, final String context,
+ final String deviceName, final ObjectNode pathsObject) {
+ final ObjectNode pathItem = JsonNodeFactory.instance.objectNode();
+ final ObjectNode operationObject = JsonNodeFactory.instance.objectNode();
+ pathItem.set("get", operationObject);
+ operationObject.put(DESCRIPTION_KEY, description);
+ operationObject.put(SUMMARY_KEY, HttpMethod.GET + SUMMARY_SEPARATOR + deviceName + SUMMARY_SEPARATOR
+ + swaggerGenerator.getResourcePathPart(resourceType));
+ operationObject.set(TAGS_KEY, buildTagsValue(Optional.of(deviceName), "GET root"));
+ final ObjectNode okResponse = JsonNodeFactory.instance.objectNode();
+ okResponse.put(DESCRIPTION_KEY, Response.Status.OK.getReasonPhrase());
+ final ObjectNode responses = JsonNodeFactory.instance.objectNode();
+ responses.set(String.valueOf(Response.Status.OK.getStatusCode()), okResponse);
+ operationObject.set(RESPONSES_KEY, responses);
+ pathsObject.set(swaggerGenerator.getResourcePath(resourceType, context), pathItem);
}
@Override
public void onMountPointCreated(final YangInstanceIdentifier path) {
- synchronized (this.lock) {
- final Long idLong = this.idKey.incrementAndGet();
- this.instanceIdToLongId.put(path, idLong);
- this.longIdToInstanceId.put(idLong, path);
+ synchronized (lock) {
+ LOG.debug("Mount point {} created", path);
+ final Long idLong = idKey.incrementAndGet();
+ instanceIdToLongId.put(path, idLong);
+ longIdToInstanceId.put(idLong, path);
}
}
@Override
public void onMountPointRemoved(final YangInstanceIdentifier path) {
- synchronized (this.lock) {
- final Long id = this.instanceIdToLongId.remove(path);
- this.longIdToInstanceId.remove(id);
- }
- }
-
- public static MountPointSwagger getInstance() {
- MountPointSwagger swagger = selfRef.get();
- if (swagger == null) {
- selfRef.compareAndSet(null, new MountPointSwagger());
- swagger = selfRef.get();
- }
- newDraft = false;
- return swagger;
- }
-
- public static MountPointSwagger getInstanceDraft18() {
- MountPointSwagger swagger = selfRef.get();
- if (swagger == null) {
- selfRef.compareAndSet(null, new MountPointSwagger());
- swagger = selfRef.get();
+ synchronized (lock) {
+ LOG.debug("Mount point {} removed", path);
+ final Long id = instanceIdToLongId.remove(path);
+ longIdToInstanceId.remove(id);
}
- newDraft = true;
- return swagger;
}
}