2 * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netconf.sal.rest.doc.impl;
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;
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;
25 import java.util.Optional;
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;
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/";
58 private MountPointSwagger swagger;
59 private UriInfo uriDeviceAll;
60 private UriInfo uriDeviceToaster;
63 public void before() throws Exception {
64 // We are sharing the global schema service and the mount schema service
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));
70 final DOMMountPointService service = mock(DOMMountPointService.class);
71 when(service.getMountPoint(INSTANCE_ID)).thenReturn(Optional.of(mountPoint));
73 swagger = new MountPointSwaggerGeneratorRFC8040(SCHEMA_SERVICE, service).getMountPointSwagger();
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());
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()
88 assertEquals(INSTANCE_URL, swagger.getInstanceIdentifiers().entrySet().iterator().next()
90 swagger.onMountPointRemoved(INSTANCE_ID); // remove ID from list of mount points
91 assertEquals(0, swagger.getInstanceIdentifiers().size());
95 public void testGetDataStoreApi() {
96 swagger.onMountPointCreated(INSTANCE_ID); // add this ID into the list of mount points
98 final SwaggerObject mountPointApi = (SwaggerObject) swagger.getMountPointApi(URI_INFO, 1L, "Datastores", "-",
100 assertNotNull("failed to find Datastore API", mountPointApi);
102 final ObjectNode pathsObject = mountPointApi.getPaths();
103 assertNotNull(pathsObject);
105 assertEquals("Unexpected api list size", 2, pathsObject.size());
107 final Set<String> actualUrls = new TreeSet<>();
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());
117 final JsonNode getOperation = operations.get("get");
119 assertNotNull("unexpected operation method on " + path, getOperation);
121 assertNotNull("expected non-null desc on " + path, getOperation.get("description"));
124 assertEquals(Set.of("/rests/data" + INSTANCE_URL + "yang-ext:mount",
125 "/rests/operations" + INSTANCE_URL + "yang-ext:mount"), actualUrls);
129 * Test that request parameters are correctly numbered.
132 * It means we should have name and name1, etc. when we have the same parameter in path multiple times.
135 public void testParametersNumberingForMountPointApi() {
136 swagger.onMountPointCreated(INSTANCE_ID);
138 final OpenApiObject mountPointApi = (OpenApiObject) swagger.getMountPointApi(URI_INFO, 1L, Optional.empty(),
140 assertNotNull("Failed to find Datastore API", mountPointApi);
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));
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));
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));
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));
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));
164 * Test that request for actions is correct and has parameters.
167 public void testActionPathsParamsForMountPointApi() {
168 swagger.onMountPointCreated(INSTANCE_ID);
170 final var mountPointApi = (OpenApiObject) swagger.getMountPointApi(URI_INFO, 1L, Optional.empty(),
172 assertNotNull("Failed to find Datastore API", mountPointApi);
174 final var pathWithParameters =
175 "/rests/operations/nodes/node=123/yang-ext:mount/action-types:list={name}/list-action";
176 assertTrue(mountPointApi.getPaths().has(pathWithParameters));
177 assertEquals(List.of("name"), getPathParameters(mountPointApi.getPaths(), pathWithParameters));
179 final var pathWithoutParameters =
180 "/rests/operations/nodes/node=123/yang-ext:mount/action-types:multi-container/inner-container/action";
181 assertTrue(mountPointApi.getPaths().has(pathWithoutParameters));
182 assertEquals(List.of(), getPathParameters(mountPointApi.getPaths(), pathWithoutParameters));
186 public void testSummaryForAllModules() {
187 swagger.onMountPointCreated(INSTANCE_ID);
188 // get OpenApiObject for the device (all modules)
189 final OpenApiObject openApiAll = (OpenApiObject) swagger.getMountPointApi(uriDeviceAll, DEVICE_ID,
190 Optional.empty(), OAversion.V3_0);
191 final var paths = openApiAll.getPaths();
192 final String getToasterSummary = openApiAll.getPaths()
193 .get(TOASTER_NODE_PATH)
197 assertEquals(TOASTER_NODE_GET_SUMMARY, getToasterSummary);
201 public void testSummaryForSingleModule() {
202 swagger.onMountPointCreated(INSTANCE_ID);
203 // get OpenApiObject for a specific module (toaster) associated with the mounted device
204 final OpenApiObject openApiToaster = (OpenApiObject) swagger.getMountPointApi(uriDeviceToaster, DEVICE_ID,
205 TOASTER, TOASTER_REVISION, OAversion.V3_0);
206 final String getToasterSummary = openApiToaster.getPaths()
207 .get(TOASTER_NODE_PATH)
211 assertEquals(TOASTER_NODE_GET_SUMMARY, getToasterSummary);
215 public void testPathsForSpecificModuleOfMounted() {
216 swagger.onMountPointCreated(INSTANCE_ID);
217 // get OpenApiObject for the device (all modules)
218 final OpenApiObject openApiAll = (OpenApiObject) swagger.getMountPointApi(uriDeviceAll, DEVICE_ID,
219 Optional.empty(), OAversion.V3_0);
220 // get OpenApiObject for a specific module (toaster(2009-11-20))
221 final OpenApiObject openApiToaster = (OpenApiObject) swagger.getMountPointApi(uriDeviceToaster, DEVICE_ID,
222 TOASTER, TOASTER_REVISION, OAversion.V3_0);
224 filter paths from openapi for all modules down to only those that are present in openapi for toaster.
225 The object for the path, that in this case ends with "yang-ext:mount" is constructed in a different way
226 when requesting OpenApiObject for a single module compared to requesting it for all modules.
227 We do not want to include it in this particular comparison, so filter it out
229 final Set<JsonNode> toasterPathsFromAll = new HashSet<>();
230 openApiAll.getPaths().fieldNames().forEachRemaining(path -> {
231 if (openApiToaster.getPaths().has(path) && !path.endsWith("yang-ext:mount")) {
232 toasterPathsFromAll.add(openApiAll.getPaths().get(path));
235 final Set<JsonNode> toasterPathsFromToaster = new HashSet<>();
236 openApiToaster.getPaths().fieldNames().forEachRemaining(path -> {
237 if (!path.endsWith("yang-ext:mount")) {
238 toasterPathsFromToaster.add(openApiToaster.getPaths().get(path));
241 // verify that the filtered set (from openapi for all modules) is the same as the set from openapi for toaster
242 assertEquals(toasterPathsFromToaster, toasterPathsFromAll);
246 * Test that checks if namespace for rpc is present.
249 public void testRpcNamespace() {
250 swagger.onMountPointCreated(INSTANCE_ID);
251 final OpenApiObject openApiToaster = (OpenApiObject) swagger.getMountPointApi(uriDeviceToaster, DEVICE_ID,
252 TOASTER, TOASTER_REVISION, OAversion.V3_0);
253 final var path = openApiToaster
254 .getPaths().get("/rests/operations/nodes/node=123/yang-ext:mount/toaster:cancel-toast");
256 final var post = path.get("post");
258 final var requestBody = post.get("requestBody");
259 assertNotNull(requestBody);
260 final var content = requestBody.get("content");
261 assertNotNull(content);
262 final var application = content.get("application/xml");
263 assertNotNull(application);
264 final var schema = application.get("schema");
265 assertNotNull(schema);
266 final var xml = schema.get("xml");
268 final var namespace = xml.get("namespace");
269 assertNotNull(namespace);
270 assertEquals("http://netconfcentral.org/ns/toaster", namespace.asText());
274 * Test that checks if namespace for actions is present.
277 public void testActionsNamespace() {
278 swagger.onMountPointCreated(INSTANCE_ID);
279 final var openApiAll = (OpenApiObject) swagger.getMountPointApi(uriDeviceAll, DEVICE_ID,
280 Optional.empty(), OAversion.V3_0);
281 final var path = openApiAll.getPaths().get(
282 "/rests/operations/nodes/node=123/yang-ext:mount/action-types:multi-container/inner-container/action");
284 final var post = path.get("post");
286 final var requestBody = post.get("requestBody");
287 assertNotNull(requestBody);
288 final var content = requestBody.get("content");
289 assertNotNull(content);
290 final var application = content.get("application/xml");
291 assertNotNull(application);
292 final var schema = application.get("schema");
293 assertNotNull(schema);
294 final var xml = schema.get("xml");
296 final var namespace = xml.get("namespace");
297 assertNotNull(namespace);
298 assertEquals("urn:ietf:params:xml:ns:yang:test:action:types", namespace.asText());
302 * Test that number of elements in payload is correct.
304 @SuppressWarnings("unchecked")
306 public void testLeafListWithMinElementsPayloadOnMountPoint() {
307 swagger.onMountPointCreated(INSTANCE_ID);
308 final var mountPointApi = (OpenApiObject) swagger.getMountPointApi(uriDeviceAll, 1L, Optional.empty(),
310 assertNotNull(mountPointApi);
311 final var paths = mountPointApi.getPaths();
313 paths.path("/rests/data/nodes/node=123/yang-ext:mount/mandatory-test:root-container/mandatory-container");
314 final var requestBody = path.path("post").path("requestBody").path("content");
315 final var jsonRef = requestBody.path("application/json").path("schema").path("$ref");
316 final var xmlRef = requestBody.path("application/xml").path("schema").path("$ref");
318 mountPointApi.getComponents().getSchemas().path("mandatory-test_root-container_mandatory-container");
319 final var minItems = schema.path("properties").path("leaf-list-with-min-elements").path("minItems");
320 final var listOfExamples = ((ArrayNode) schema.path("properties").path("leaf-list-with-min-elements")
322 final var expectedListOfExamples = JsonNodeFactory.instance.arrayNode()
323 .add("Some leaf-list-with-min-elements")
324 .add("Some leaf-list-with-min-elements");
325 assertFalse(listOfExamples.isMissingNode());
326 assertEquals(jsonRef, xmlRef);
327 assertEquals(2, minItems.intValue());
328 assertEquals(expectedListOfExamples, listOfExamples);