Change alignments in methods declaration
[netconf.git] / restconf / sal-rest-docgen / src / main / java / org / opendaylight / netconf / sal / rest / doc / mountpoints / MountPointSwagger.java
1 /*
2  * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.sal.rest.doc.mountpoints;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.DEFAULT_PAGESIZE;
13 import static org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator.BASE_PATH;
14 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.DESCRIPTION_KEY;
15 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.RESPONSES_KEY;
16 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.SUMMARY_KEY;
17 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.SUMMARY_SEPARATOR;
18 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.TAGS_KEY;
19 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.buildTagsValue;
20 import static org.opendaylight.netconf.sal.rest.doc.util.JsonUtil.addFields;
21
22 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
23 import com.fasterxml.jackson.databind.node.ObjectNode;
24 import com.google.common.collect.Range;
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Optional;
29 import java.util.TreeMap;
30 import java.util.concurrent.atomic.AtomicLong;
31 import javax.ws.rs.HttpMethod;
32 import javax.ws.rs.core.Response;
33 import javax.ws.rs.core.UriInfo;
34 import org.opendaylight.mdsal.dom.api.DOMMountPointListener;
35 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
36 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
37 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
38 import org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator;
39 import org.opendaylight.netconf.sal.rest.doc.impl.DefinitionNames;
40 import org.opendaylight.netconf.sal.rest.doc.swagger.CommonApiObject;
41 import org.opendaylight.netconf.sal.rest.doc.swagger.SwaggerObject;
42 import org.opendaylight.yangtools.concepts.ListenerRegistration;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
45 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
46 import org.opendaylight.yangtools.yang.model.api.Module;
47 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 public class MountPointSwagger implements DOMMountPointListener, AutoCloseable {
52
53     private static final Logger LOG = LoggerFactory.getLogger(MountPointSwagger.class);
54
55     private static final String DATASTORES_REVISION = "-";
56     private static final String DATASTORES_LABEL = "Datastores";
57
58     private final DOMSchemaService globalSchema;
59     private final DOMMountPointService mountService;
60     private final BaseYangSwaggerGenerator swaggerGenerator;
61     private final Map<YangInstanceIdentifier, Long> instanceIdToLongId =
62             new TreeMap<>((o1, o2) -> o1.toString().compareToIgnoreCase(o2.toString()));
63     private final Map<Long, YangInstanceIdentifier> longIdToInstanceId = new HashMap<>();
64
65     private final Object lock = new Object();
66
67     private final AtomicLong idKey = new AtomicLong(0);
68
69     private ListenerRegistration<DOMMountPointListener> registration;
70
71     public MountPointSwagger(final DOMSchemaService globalSchema, final DOMMountPointService mountService,
72             final BaseYangSwaggerGenerator swaggerGenerator) {
73         this.globalSchema = requireNonNull(globalSchema);
74         this.mountService = requireNonNull(mountService);
75         this.swaggerGenerator = requireNonNull(swaggerGenerator);
76     }
77
78     public void init() {
79         registration = mountService.registerProvisionListener(this);
80     }
81
82     @Override
83     public void close() {
84         if (registration != null) {
85             registration.close();
86         }
87     }
88
89     public Map<String, Long> getInstanceIdentifiers() {
90         final Map<String, Long> urlToId = new HashMap<>();
91         synchronized (lock) {
92             final SchemaContext context = globalSchema.getGlobalContext();
93             for (final Entry<YangInstanceIdentifier, Long> entry : instanceIdToLongId.entrySet()) {
94                 final String modName = findModuleName(entry.getKey(), context);
95                 urlToId.put(swaggerGenerator.generateUrlPrefixFromInstanceID(entry.getKey(), modName),
96                         entry.getValue());
97             }
98         }
99         return urlToId;
100     }
101
102     private static String findModuleName(final YangInstanceIdentifier id, final SchemaContext context) {
103         final PathArgument rootQName = id.getPathArguments().iterator().next();
104         for (final Module mod : context.getModules()) {
105             if (mod.findDataChildByName(rootQName.getNodeType()).isPresent()) {
106                 return mod.getName();
107             }
108         }
109         return null;
110     }
111
112     private String getYangMountUrl(final YangInstanceIdentifier key) {
113         final String modName = findModuleName(key, globalSchema.getGlobalContext());
114         return swaggerGenerator.generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount";
115     }
116
117     private YangInstanceIdentifier getInstanceId(final Long id) {
118         final YangInstanceIdentifier instanceId;
119         synchronized (lock) {
120             instanceId = longIdToInstanceId.get(id);
121         }
122         return instanceId;
123     }
124
125     private EffectiveModelContext getSchemaContext(final YangInstanceIdentifier id) {
126         if (id == null) {
127             return null;
128         }
129
130         checkState(mountService != null);
131         return mountService.getMountPoint(id)
132             .flatMap(mountPoint -> mountPoint.getService(DOMSchemaService.class))
133             .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
134             .orElse(null);
135     }
136
137     public CommonApiObject getMountPointApi(final UriInfo uriInfo, final Long id, final String module,
138             final String revision, final OAversion oaversion) {
139         final YangInstanceIdentifier iid = getInstanceId(id);
140         final EffectiveModelContext context = getSchemaContext(iid);
141         final String urlPrefix = getYangMountUrl(iid);
142         final String deviceName  = extractDeviceName(iid);
143
144         if (context == null) {
145             return null;
146         }
147
148         if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
149             return generateDataStoreApiDoc(uriInfo, urlPrefix, deviceName);
150         }
151         final SwaggerObject swaggerObject = swaggerGenerator.getApiDeclaration(module, revision, uriInfo, context,
152                 urlPrefix, oaversion);
153         return BaseYangSwaggerGenerator.getAppropriateDoc(swaggerObject, oaversion);
154     }
155
156     public CommonApiObject getMountPointApi(final UriInfo uriInfo, final Long id, final Optional<Integer> pageNum,
157             final OAversion oaversion) {
158         final YangInstanceIdentifier iid = getInstanceId(id);
159         final EffectiveModelContext context = getSchemaContext(iid);
160         final String urlPrefix = getYangMountUrl(iid);
161         final String deviceName  = extractDeviceName(iid);
162
163         if (context == null) {
164             return null;
165         }
166         final DefinitionNames definitionNames = new DefinitionNames();
167
168         boolean includeDataStore = true;
169         Optional<Range<Integer>> range = Optional.empty();
170
171         if (pageNum.isPresent()) {
172             final int pageNumValue = pageNum.get();
173             final int end = DEFAULT_PAGESIZE * pageNumValue - 1;
174             int start = end - DEFAULT_PAGESIZE;
175             if (pageNumValue == 1) {
176                 start++;
177             } else {
178                 includeDataStore = false;
179             }
180             range = Optional.of(Range.closed(start, end));
181         }
182
183         final SwaggerObject doc;
184
185         final SwaggerObject swaggerObject = swaggerGenerator.getAllModulesDoc(uriInfo, range, context,
186                 Optional.of(deviceName), urlPrefix, definitionNames, oaversion);
187
188         if (includeDataStore) {
189             doc = generateDataStoreApiDoc(uriInfo, urlPrefix, deviceName);
190             addFields(doc.getPaths() ,swaggerObject.getPaths().fields());
191             addFields(doc.getDefinitions() ,swaggerObject.getDefinitions().fields());
192             doc.getInfo().setTitle(swaggerObject.getInfo().getTitle());
193         } else {
194             doc = swaggerObject;
195         }
196
197         return BaseYangSwaggerGenerator.getAppropriateDoc(doc, oaversion);
198     }
199
200     private static String extractDeviceName(final YangInstanceIdentifier iid) {
201         return ((YangInstanceIdentifier.NodeIdentifierWithPredicates.Singleton)iid.getLastPathArgument())
202                 .values().getElement().toString();
203     }
204
205     private SwaggerObject generateDataStoreApiDoc(final UriInfo uriInfo, final String context,
206             final String deviceName) {
207         final SwaggerObject declaration = swaggerGenerator.createSwaggerObject(
208                 swaggerGenerator.createSchemaFromUriInfo(uriInfo),
209                 swaggerGenerator.createHostFromUriInfo(uriInfo),
210                 BASE_PATH,
211                 context);
212
213         final ObjectNode pathsObject = JsonNodeFactory.instance.objectNode();
214         createGetPathItem("config", "Queries the config (startup) datastore on the mounted hosted.",
215                 context, deviceName, pathsObject);
216         createGetPathItem("operational", "Queries the operational (running) datastore on the mounted hosted.",
217                 context, deviceName, pathsObject);
218         createGetPathItem("operations", "Queries the available operations (RPC calls) on the mounted hosted.",
219                 context, deviceName, pathsObject);
220
221         declaration.setPaths(pathsObject);
222         declaration.setDefinitions(JsonNodeFactory.instance.objectNode());
223
224         return declaration;
225     }
226
227     private void createGetPathItem(final String resourceType, final String description, final String context,
228             final String deviceName, final ObjectNode pathsObject) {
229         final ObjectNode pathItem = JsonNodeFactory.instance.objectNode();
230         final ObjectNode operationObject = JsonNodeFactory.instance.objectNode();
231         pathItem.set("get", operationObject);
232         operationObject.put(DESCRIPTION_KEY, description);
233         operationObject.put(SUMMARY_KEY, HttpMethod.GET + SUMMARY_SEPARATOR + deviceName + SUMMARY_SEPARATOR
234                 + swaggerGenerator.getResourcePathPart(resourceType));
235         operationObject.set(TAGS_KEY, buildTagsValue(Optional.of(deviceName), "GET root"));
236         final ObjectNode okResponse = JsonNodeFactory.instance.objectNode();
237         okResponse.put(DESCRIPTION_KEY, Response.Status.OK.getReasonPhrase());
238         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
239         responses.set(String.valueOf(Response.Status.OK.getStatusCode()), okResponse);
240         operationObject.set(RESPONSES_KEY, responses);
241         pathsObject.set(swaggerGenerator.getResourcePath(resourceType, context), pathItem);
242     }
243
244     @Override
245     public void onMountPointCreated(final YangInstanceIdentifier path) {
246         synchronized (lock) {
247             LOG.debug("Mount point {} created", path);
248             final Long idLong = idKey.incrementAndGet();
249             instanceIdToLongId.put(path, idLong);
250             longIdToInstanceId.put(idLong, path);
251         }
252     }
253
254     @Override
255     public void onMountPointRemoved(final YangInstanceIdentifier path) {
256         synchronized (lock) {
257             LOG.debug("Mount point {} removed", path);
258             final Long id = instanceIdToLongId.remove(path);
259             longIdToInstanceId.remove(id);
260         }
261     }
262 }