c1db115ba1353bd41d662d865d18703ec53255ab
[netconf.git] / restconf / sal-rest-docgen / src / test / java / org / opendaylight / netconf / sal / rest / doc / impl / MountPointSwaggerTest.java
1 /*
2  * Copyright (c) 2014 Brocade Communications Systems, Inc. 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.impl;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.Mockito.mock;
15 import static org.mockito.Mockito.when;
16
17 import com.fasterxml.jackson.databind.JsonNode;
18 import com.fasterxml.jackson.databind.node.ArrayNode;
19 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
20 import com.fasterxml.jackson.databind.node.ObjectNode;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.Set;
27 import java.util.TreeSet;
28 import javax.ws.rs.core.UriInfo;
29 import org.glassfish.jersey.internal.util.collection.ImmutableMultivaluedMap;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
33 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
34 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
35 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
36 import org.opendaylight.netconf.sal.rest.doc.mountpoints.MountPointSwagger;
37 import org.opendaylight.netconf.sal.rest.doc.swagger.OpenApiObject;
38 import org.opendaylight.netconf.sal.rest.doc.swagger.SwaggerObject;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41
42 public final class MountPointSwaggerTest extends AbstractApiDocTest {
43     private static final String TOASTER = "toaster";
44     private static final String TOASTER_REVISION = "2009-11-20";
45     private static final Long DEVICE_ID = 1L;
46     private static final String DEVICE_NAME = "123";
47     private static final String TOASTER_NODE_PATH = "/rests/data/nodes/node=123/yang-ext:mount/toaster:toaster";
48     private static final String TOASTER_NODE_GET_SUMMARY = "GET - " + DEVICE_NAME + " - toaster - toaster";
49     private static final String GET_ALL = "http://localhost:8181/openapi/api/v3/mounts/" + DEVICE_ID;
50     private static final String GET_TOASTER = "http://localhost:8181/openapi/api/v3/mounts/%s/%s(%s)".formatted(
51             DEVICE_ID, TOASTER, TOASTER_REVISION);
52     private static final YangInstanceIdentifier INSTANCE_ID = YangInstanceIdentifier.builder()
53             .node(QName.create("", "nodes"))
54             .node(QName.create("", "node"))
55             .nodeWithKey(QName.create("", "node"), QName.create("", "id"), DEVICE_NAME).build();
56     private static final String INSTANCE_URL = "/nodes/node=123/";
57
58     private MountPointSwagger swagger;
59     private UriInfo uriDeviceAll;
60     private UriInfo uriDeviceToaster;
61
62     @Before
63     public void before() throws Exception {
64         // We are sharing the global schema service and the mount schema service
65         // in our test.
66         // OK for testing - real thing would have separate instances.
67         final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
68         when(mountPoint.getService(DOMSchemaService.class)).thenReturn(Optional.of(SCHEMA_SERVICE));
69
70         final DOMMountPointService service = mock(DOMMountPointService.class);
71         when(service.getMountPoint(INSTANCE_ID)).thenReturn(Optional.of(mountPoint));
72
73         swagger = new MountPointSwaggerGeneratorRFC8040(SCHEMA_SERVICE, service).getMountPointSwagger();
74
75         uriDeviceAll = DocGenTestHelper.createMockUriInfo(GET_ALL);
76         when(uriDeviceAll.getQueryParameters()).thenReturn(ImmutableMultivaluedMap.empty());
77         uriDeviceToaster = DocGenTestHelper.createMockUriInfo(GET_TOASTER);
78         when(uriDeviceToaster.getQueryParameters()).thenReturn(ImmutableMultivaluedMap.empty());
79     }
80
81     @Test()
82     public void getInstanceIdentifiers() {
83         assertEquals(0, swagger.getInstanceIdentifiers().size());
84         swagger.onMountPointCreated(INSTANCE_ID); // add this ID into the list of mount points
85         assertEquals(1, swagger.getInstanceIdentifiers().size());
86         assertEquals((Long) 1L, swagger.getInstanceIdentifiers().entrySet().iterator().next()
87                 .getValue());
88         assertEquals(INSTANCE_URL, swagger.getInstanceIdentifiers().entrySet().iterator().next()
89                 .getKey());
90         swagger.onMountPointRemoved(INSTANCE_ID); // remove ID from list of mount points
91         assertEquals(0, swagger.getInstanceIdentifiers().size());
92     }
93
94     @Test
95     public void testGetDataStoreApi() {
96         swagger.onMountPointCreated(INSTANCE_ID); // add this ID into the list of mount points
97
98         final SwaggerObject mountPointApi = (SwaggerObject) swagger.getMountPointApi(URI_INFO, 1L, "Datastores", "-",
99             OAversion.V2_0);
100         assertNotNull("failed to find Datastore API", mountPointApi);
101
102         final ObjectNode pathsObject = mountPointApi.getPaths();
103         assertNotNull(pathsObject);
104
105         assertEquals("Unexpected api list size", 2, pathsObject.size());
106
107         final Set<String> actualUrls = new TreeSet<>();
108
109         final Iterator<Map.Entry<String, JsonNode>> fields = pathsObject.fields();
110         while (fields.hasNext()) {
111             final Map.Entry<String, JsonNode> field = fields.next();
112             final String path = field.getKey();
113             final JsonNode operations = field.getValue();
114             actualUrls.add(field.getKey());
115             assertEquals("unexpected operations size on " + path, 1, operations.size());
116
117             final JsonNode getOperation = operations.get("get");
118
119             assertNotNull("unexpected operation method on " + path, getOperation);
120
121             assertNotNull("expected non-null desc on " + path, getOperation.get("description"));
122         }
123
124         assertEquals(Set.of("/rests/data" + INSTANCE_URL + "yang-ext:mount",
125             "/rests/operations" + INSTANCE_URL + "yang-ext:mount"), actualUrls);
126     }
127
128     /**
129      * Test that request parameters are correctly numbered.
130      *
131      * <p>
132      * It means we should have name and name1, etc. when we have the same parameter in path multiple times.
133      */
134     @Test
135     public void testParametersNumberingForMountPointApi() {
136         swagger.onMountPointCreated(INSTANCE_ID);
137
138         final OpenApiObject mountPointApi = (OpenApiObject) swagger.getMountPointApi(URI_INFO, 1L, Optional.empty(),
139                 OAversion.V3_0);
140         assertNotNull("Failed to find Datastore API", mountPointApi);
141
142         var pathToList1 = "/rests/data/nodes/node=123/yang-ext:mount/path-params-test:cont/list1={name}";
143         assertTrue(mountPointApi.getPaths().has(pathToList1));
144         assertEquals(List.of("name"), getPathParameters(mountPointApi.getPaths(), pathToList1));
145
146         var pathToList2 = "/rests/data/nodes/node=123/yang-ext:mount/path-params-test:cont/list1={name}/list2={name1}";
147         assertTrue(mountPointApi.getPaths().has(pathToList2));
148         assertEquals(List.of("name", "name1"), getPathParameters(mountPointApi.getPaths(), pathToList2));
149
150         var pathToList3 = "/rests/data/nodes/node=123/yang-ext:mount/path-params-test:cont/list3={name}";
151         assertTrue(mountPointApi.getPaths().has(pathToList3));
152         assertEquals(List.of("name"), getPathParameters(mountPointApi.getPaths(), pathToList3));
153
154         var pathToList4 = "/rests/data/nodes/node=123/yang-ext:mount/path-params-test:cont/list1={name}/list4={name1}";
155         assertTrue(mountPointApi.getPaths().has(pathToList4));
156         assertEquals(List.of("name", "name1"), getPathParameters(mountPointApi.getPaths(), pathToList4));
157
158         var pathToList5 = "/rests/data/nodes/node=123/yang-ext:mount/path-params-test:cont/list1={name}/cont2";
159         assertTrue(mountPointApi.getPaths().has(pathToList5));
160         assertEquals(List.of("name"), getPathParameters(mountPointApi.getPaths(), pathToList5));
161     }
162
163     /**
164      * Test that request parameters are correctly typed.
165      */
166     @Test
167     public void testParametersTypesForMountPointApi() throws Exception {
168         swagger.onMountPointCreated(INSTANCE_ID);
169         final var doc = (OpenApiObject) swagger.getMountPointApi(URI_INFO, 1L, Optional.empty(),
170             OAversion.V3_0);
171         final var pathToContainer = "/rests/data/nodes/node=123/yang-ext:mount/typed-params:typed/";
172         final var integerTypes = List.of("uint64", "uint32", "uint16", "uint8", "int64", "int32", "int16", "int8");
173         for (final var type: integerTypes) {
174             final var typeKey = type + "-key";
175             final var path = pathToContainer + type + "={" + typeKey + "}";
176             assertTrue(doc.getPaths().has(path));
177             assertEquals("integer", doc.getPaths().get(path).get("get").get("parameters").get(0).get("schema")
178                 .get("type").textValue());
179         }
180     }
181
182     /**
183      * Test that request for actions is correct and has parameters.
184      */
185     @Test
186     public void testActionPathsParamsForMountPointApi() {
187         swagger.onMountPointCreated(INSTANCE_ID);
188
189         final var mountPointApi = (OpenApiObject) swagger.getMountPointApi(URI_INFO, 1L, Optional.empty(),
190             OAversion.V3_0);
191         assertNotNull("Failed to find Datastore API", mountPointApi);
192
193         final var pathWithParameters =
194             "/rests/operations/nodes/node=123/yang-ext:mount/action-types:list={name}/list-action";
195         assertTrue(mountPointApi.getPaths().has(pathWithParameters));
196         assertEquals(List.of("name"), getPathParameters(mountPointApi.getPaths(), pathWithParameters));
197
198         final var pathWithoutParameters =
199             "/rests/operations/nodes/node=123/yang-ext:mount/action-types:multi-container/inner-container/action";
200         assertTrue(mountPointApi.getPaths().has(pathWithoutParameters));
201         assertEquals(List.of(), getPathParameters(mountPointApi.getPaths(), pathWithoutParameters));
202     }
203
204     @Test
205     public void testSummaryForAllModules() {
206         swagger.onMountPointCreated(INSTANCE_ID);
207         // get OpenApiObject for the device (all modules)
208         final OpenApiObject openApiAll = (OpenApiObject) swagger.getMountPointApi(uriDeviceAll, DEVICE_ID,
209             Optional.empty(), OAversion.V3_0);
210         final var paths = openApiAll.getPaths();
211         final String getToasterSummary = openApiAll.getPaths()
212             .get(TOASTER_NODE_PATH)
213             .get("get")
214             .get("summary")
215             .asText();
216         assertEquals(TOASTER_NODE_GET_SUMMARY, getToasterSummary);
217     }
218
219     @Test
220     public void testSummaryForSingleModule() {
221         swagger.onMountPointCreated(INSTANCE_ID);
222         // get OpenApiObject for a specific module (toaster) associated with the mounted device
223         final OpenApiObject openApiToaster = (OpenApiObject) swagger.getMountPointApi(uriDeviceToaster, DEVICE_ID,
224             TOASTER, TOASTER_REVISION, OAversion.V3_0);
225         final String getToasterSummary = openApiToaster.getPaths()
226             .get(TOASTER_NODE_PATH)
227             .get("get")
228             .get("summary")
229             .asText();
230         assertEquals(TOASTER_NODE_GET_SUMMARY, getToasterSummary);
231     }
232
233     @Test
234     public void testPathsForSpecificModuleOfMounted() {
235         swagger.onMountPointCreated(INSTANCE_ID);
236         // get OpenApiObject for the device (all modules)
237         final OpenApiObject openApiAll = (OpenApiObject) swagger.getMountPointApi(uriDeviceAll, DEVICE_ID,
238             Optional.empty(), OAversion.V3_0);
239         // get OpenApiObject for a specific module (toaster(2009-11-20))
240         final OpenApiObject openApiToaster = (OpenApiObject) swagger.getMountPointApi(uriDeviceToaster, DEVICE_ID,
241             TOASTER, TOASTER_REVISION, OAversion.V3_0);
242         /*
243             filter paths from openapi for all modules down to only those that are present in openapi for toaster.
244             The object for the path, that in this case ends with "yang-ext:mount" is constructed in a different way
245             when requesting OpenApiObject for a single module compared to requesting it for all modules.
246             We do not want to include it in this particular comparison, so filter it out
247          */
248         final Set<JsonNode> toasterPathsFromAll = new HashSet<>();
249         openApiAll.getPaths().fieldNames().forEachRemaining(path -> {
250             if (openApiToaster.getPaths().has(path) && !path.endsWith("yang-ext:mount")) {
251                 toasterPathsFromAll.add(openApiAll.getPaths().get(path));
252             }
253         });
254         final Set<JsonNode> toasterPathsFromToaster = new HashSet<>();
255         openApiToaster.getPaths().fieldNames().forEachRemaining(path -> {
256             if (!path.endsWith("yang-ext:mount")) {
257                 toasterPathsFromToaster.add(openApiToaster.getPaths().get(path));
258             }
259         });
260         // verify that the filtered set (from openapi for all modules) is the same as the set from openapi for toaster
261         assertEquals(toasterPathsFromToaster, toasterPathsFromAll);
262     }
263
264     /**
265      * Test that checks if namespace for rpc is present.
266      */
267     @Test
268     public void testRpcNamespace() {
269         swagger.onMountPointCreated(INSTANCE_ID);
270         final OpenApiObject openApiToaster = (OpenApiObject) swagger.getMountPointApi(uriDeviceToaster, DEVICE_ID,
271             TOASTER, TOASTER_REVISION, OAversion.V3_0);
272         final var path = openApiToaster
273             .getPaths().get("/rests/operations/nodes/node=123/yang-ext:mount/toaster:cancel-toast");
274         assertNotNull(path);
275         final var post = path.get("post");
276         assertNotNull(post);
277         final var requestBody = post.get("requestBody");
278         assertNotNull(requestBody);
279         final var content = requestBody.get("content");
280         assertNotNull(content);
281         final var application = content.get("application/xml");
282         assertNotNull(application);
283         final var schema = application.get("schema");
284         assertNotNull(schema);
285         final var xml = schema.get("xml");
286         assertNotNull(xml);
287         final var namespace = xml.get("namespace");
288         assertNotNull(namespace);
289         assertEquals("http://netconfcentral.org/ns/toaster", namespace.asText());
290     }
291
292     /**
293      * Test that checks if namespace for actions is present.
294      */
295     @Test
296     public void testActionsNamespace() {
297         swagger.onMountPointCreated(INSTANCE_ID);
298         final var openApiAll = (OpenApiObject) swagger.getMountPointApi(uriDeviceAll, DEVICE_ID,
299             Optional.empty(), OAversion.V3_0);
300         final var path = openApiAll.getPaths().get(
301             "/rests/operations/nodes/node=123/yang-ext:mount/action-types:multi-container/inner-container/action");
302         assertNotNull(path);
303         final var post = path.get("post");
304         assertNotNull(post);
305         final var requestBody = post.get("requestBody");
306         assertNotNull(requestBody);
307         final var content = requestBody.get("content");
308         assertNotNull(content);
309         final var application = content.get("application/xml");
310         assertNotNull(application);
311         final var schema = application.get("schema");
312         assertNotNull(schema);
313         final var xml = schema.get("xml");
314         assertNotNull(xml);
315         final var namespace = xml.get("namespace");
316         assertNotNull(namespace);
317         assertEquals("urn:ietf:params:xml:ns:yang:test:action:types", namespace.asText());
318     }
319
320     /**
321      * Test that number of elements in payload is correct.
322      */
323     @SuppressWarnings("unchecked")
324     @Test
325     public void testLeafListWithMinElementsPayloadOnMountPoint() {
326         swagger.onMountPointCreated(INSTANCE_ID);
327         final var mountPointApi = (OpenApiObject) swagger.getMountPointApi(uriDeviceAll, 1L, Optional.empty(),
328             OAversion.V3_0);
329         assertNotNull(mountPointApi);
330         final var paths = mountPointApi.getPaths();
331         final var path =
332             paths.path("/rests/data/nodes/node=123/yang-ext:mount/mandatory-test:root-container/mandatory-container");
333         final var requestBody = path.path("post").path("requestBody").path("content");
334         final var jsonRef = requestBody.path("application/json").path("schema").path("$ref");
335         final var xmlRef = requestBody.path("application/xml").path("schema").path("$ref");
336         final var schema =
337             mountPointApi.getComponents().getSchemas().path("mandatory-test_root-container_mandatory-container");
338         final var minItems = schema.path("properties").path("leaf-list-with-min-elements").path("minItems");
339         final var listOfExamples = ((ArrayNode) schema.path("properties").path("leaf-list-with-min-elements")
340             .path("example"));
341         final var expectedListOfExamples = JsonNodeFactory.instance.arrayNode()
342             .add("Some leaf-list-with-min-elements")
343             .add("Some leaf-list-with-min-elements");
344         assertFalse(listOfExamples.isMissingNode());
345         assertEquals(jsonRef, xmlRef);
346         assertEquals(2, minItems.intValue());
347         assertEquals(expectedListOfExamples, listOfExamples);
348     }
349 }