Adapt API to OpenApiObject removal
[netconf.git] / restconf / restconf-openapi / src / main / java / org / opendaylight / restconf / openapi / mountpoints / MountPointOpenApi.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.restconf.openapi.mountpoints;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.BASE_PATH;
13 import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.SECURITY;
14
15 import java.io.IOException;
16 import java.util.HashMap;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.TreeMap;
20 import java.util.concurrent.atomic.AtomicLong;
21 import javax.ws.rs.core.UriInfo;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.mdsal.dom.api.DOMMountPointListener;
24 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
25 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
26 import org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator;
27 import org.opendaylight.restconf.openapi.impl.OpenApiInputStream;
28 import org.opendaylight.yangtools.concepts.ListenerRegistration;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
32 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
33 import org.opendaylight.yangtools.yang.model.api.Module;
34 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 public class MountPointOpenApi implements DOMMountPointListener, AutoCloseable {
39
40     private static final Logger LOG = LoggerFactory.getLogger(MountPointOpenApi.class);
41
42     private static final String DATASTORES_REVISION = "-";
43     private static final String DATASTORES_LABEL = "Datastores";
44
45     private final DOMSchemaService globalSchema;
46     private final DOMMountPointService mountService;
47     private final BaseYangOpenApiGenerator openApiGenerator;
48     private final Map<YangInstanceIdentifier, Long> instanceIdToLongId =
49             new TreeMap<>((o1, o2) -> o1.toString().compareToIgnoreCase(o2.toString()));
50     private final Map<Long, YangInstanceIdentifier> longIdToInstanceId = new HashMap<>();
51
52     private final Object lock = new Object();
53
54     private final AtomicLong idKey = new AtomicLong(0);
55
56     private ListenerRegistration<DOMMountPointListener> registration;
57
58     public MountPointOpenApi(final DOMSchemaService globalSchema, final DOMMountPointService mountService,
59             final BaseYangOpenApiGenerator openApiGenerator) {
60         this.globalSchema = requireNonNull(globalSchema);
61         this.mountService = requireNonNull(mountService);
62         this.openApiGenerator = requireNonNull(openApiGenerator);
63     }
64
65     public void init() {
66         registration = mountService.registerProvisionListener(this);
67     }
68
69     @Override
70     public void close() {
71         if (registration != null) {
72             registration.close();
73         }
74     }
75
76     public Map<String, Long> getInstanceIdentifiers() {
77         final Map<String, Long> urlToId = new HashMap<>();
78         synchronized (lock) {
79             final SchemaContext context = globalSchema.getGlobalContext();
80             for (final Entry<YangInstanceIdentifier, Long> entry : instanceIdToLongId.entrySet()) {
81                 final String modName = findModuleName(entry.getKey(), context);
82                 urlToId.put(generateUrlPrefixFromInstanceID(entry.getKey(), modName), entry.getValue());
83             }
84         }
85         return urlToId;
86     }
87
88     private static String findModuleName(final YangInstanceIdentifier id, final SchemaContext context) {
89         final PathArgument rootQName = id.getPathArguments().iterator().next();
90         for (final Module mod : context.getModules()) {
91             if (mod.findDataChildByName(rootQName.getNodeType()).isPresent()) {
92                 return mod.getName();
93             }
94         }
95         return null;
96     }
97
98     private String getYangMountUrl(final YangInstanceIdentifier key) {
99         final String modName = findModuleName(key, globalSchema.getGlobalContext());
100         return generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount";
101     }
102
103     private static String generateUrlPrefixFromInstanceID(final YangInstanceIdentifier key, final String moduleName) {
104         final StringBuilder builder = new StringBuilder();
105         builder.append("/");
106         if (moduleName != null) {
107             builder.append(moduleName).append(':');
108         }
109         for (final PathArgument arg : key.getPathArguments()) {
110             final String name = arg.getNodeType().getLocalName();
111             if (arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates nodeId) {
112                 for (final Entry<QName, Object> entry : nodeId.entrySet()) {
113                     builder.deleteCharAt(builder.length() - 1).append("=").append(entry.getValue()).append('/');
114                 }
115             } else {
116                 builder.append(name).append('/');
117             }
118         }
119         return builder.toString();
120     }
121
122     private YangInstanceIdentifier getInstanceId(final Long id) {
123         final YangInstanceIdentifier instanceId;
124         synchronized (lock) {
125             instanceId = longIdToInstanceId.get(id);
126         }
127         return instanceId;
128     }
129
130     private EffectiveModelContext getSchemaContext(final YangInstanceIdentifier id) {
131         if (id == null) {
132             return null;
133         }
134
135         checkState(mountService != null);
136         return mountService.getMountPoint(id)
137             .flatMap(mountPoint -> mountPoint.getService(DOMSchemaService.class))
138             .map(DOMSchemaService::getGlobalContext)
139             .orElse(null);
140     }
141
142     public OpenApiInputStream getMountPointApi(final UriInfo uriInfo, final Long id, final String module,
143             final String revision) throws IOException  {
144         final YangInstanceIdentifier iid = getInstanceId(id);
145         final EffectiveModelContext context = getSchemaContext(iid);
146         final String urlPrefix = getYangMountUrl(iid);
147         final String deviceName = extractDeviceName(iid);
148
149         if (context == null) {
150             return null;
151         }
152
153         if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
154             return generateDataStoreOpenApi(context, uriInfo, urlPrefix, deviceName);
155         }
156         return openApiGenerator.getApiDeclaration(module, revision, uriInfo, context, urlPrefix, deviceName);
157     }
158
159     public OpenApiInputStream getMountPointApi(final UriInfo uriInfo, final Long id, final @Nullable String strPageNum)
160             throws IOException {
161         final var iid = getInstanceId(id);
162         final var context = getSchemaContext(iid);
163         final var urlPrefix = getYangMountUrl(iid);
164         final var deviceName = extractDeviceName(iid);
165
166         if (context == null) {
167             return null;
168         }
169
170         boolean includeDataStore = true;
171         if (strPageNum != null) {
172             final var pageNum = Integer.parseInt(strPageNum);
173             if (pageNum != 1) {
174                 includeDataStore = false;
175             }
176         }
177
178         final var schema = openApiGenerator.createSchemaFromUriInfo(uriInfo);
179         final var host = openApiGenerator.createHostFromUriInfo(uriInfo);
180         final var title = deviceName + " modules of RESTCONF";
181         final var url = schema + "://" + host + BASE_PATH;
182         final var modules = context.getModules();
183         return new OpenApiInputStream(context, title, url, SECURITY, deviceName, urlPrefix, false, includeDataStore,
184             modules);
185     }
186
187     private static String extractDeviceName(final YangInstanceIdentifier iid) {
188         return ((YangInstanceIdentifier.NodeIdentifierWithPredicates.Singleton)iid.getLastPathArgument())
189                 .values().getElement().toString();
190     }
191
192     private OpenApiInputStream generateDataStoreOpenApi(EffectiveModelContext modelContext,
193             final UriInfo uriInfo, final String urlPrefix, final String deviceName) throws IOException {
194         final var schema = openApiGenerator.createSchemaFromUriInfo(uriInfo);
195         final var host = openApiGenerator.createHostFromUriInfo(uriInfo);
196         final var url = schema + "://" + host + BASE_PATH;
197         final var modules = modelContext.getModules();
198         return new OpenApiInputStream(modelContext, urlPrefix, url, SECURITY, deviceName, urlPrefix, true, false,
199             modules);
200     }
201
202     @Override
203     public void onMountPointCreated(final YangInstanceIdentifier path) {
204         synchronized (lock) {
205             LOG.debug("Mount point {} created", path);
206             final Long idLong = idKey.incrementAndGet();
207             instanceIdToLongId.put(path, idLong);
208             longIdToInstanceId.put(idLong, path);
209         }
210     }
211
212     @Override
213     public void onMountPointRemoved(final YangInstanceIdentifier path) {
214         synchronized (lock) {
215             LOG.debug("Mount point {} removed", path);
216             final Long id = instanceIdToLongId.remove(path);
217             longIdToInstanceId.remove(id);
218         }
219     }
220 }