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