From 1d64392feb896e9c74f4d9085eea52bbfdac1211 Mon Sep 17 00:00:00 2001 From: Yaroslav Lastivka Date: Wed, 26 Jul 2023 18:18:03 +0300 Subject: [PATCH] OpenApi: Remove incorrect list POST requests Our logic creates POST requests for lists with keys, containing multiple resources in payload. This does not align with the expected structure for creating child resources in the YANG model. Added condition that prevents the creation of POST requests that has list as a last element. JIRA: NETCONF-1101 Change-Id: I46820f222c9c5ef8078ac8675d2adc12b06f1253 Signed-off-by: Yaroslav Lastivka Signed-off-by: Ivan Hrasko --- .../impl/BaseYangOpenApiGenerator.java | 6 +- .../openapi/impl/ListPostRequestsTest.java | 76 +++++++++++++++++++ .../impl/OpenApiGeneratorRFC8040Test.java | 23 +++--- .../openapi/impl/PostPayloadTest.java | 15 ---- .../mountpoints/MountPointOpenApiTest.java | 12 +-- 5 files changed, 100 insertions(+), 32 deletions(-) create mode 100644 restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/ListPostRequestsTest.java diff --git a/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/BaseYangOpenApiGenerator.java b/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/BaseYangOpenApiGenerator.java index e004cb5ff8..0a2397765f 100644 --- a/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/BaseYangOpenApiGenerator.java +++ b/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/BaseYangOpenApiGenerator.java @@ -319,9 +319,11 @@ public abstract class BaseYangOpenApiGenerator { final Operation delete = buildDelete(node, moduleName, deviceName, pathParams); operationsBuilder.delete(delete); - final Operation post = buildPost(node, parentName, nodeName, discriminator, moduleName, deviceName, + if (!(node instanceof ListSchemaNode)) { + final Operation post = buildPost(node, parentName, nodeName, discriminator, moduleName, deviceName, node.getDescription().orElse(""), pathParams); - operationsBuilder.post(post); + operationsBuilder.post(post); + } } return operationsBuilder.build(); } diff --git a/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/ListPostRequestsTest.java b/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/ListPostRequestsTest.java new file mode 100644 index 0000000000..75dfe90106 --- /dev/null +++ b/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/ListPostRequestsTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.restconf.openapi.impl; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.restconf.openapi.DocGenTestHelper; +import org.opendaylight.restconf.openapi.model.OpenApiObject; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public class ListPostRequestsTest { + private static OpenApiObject doc; + + @BeforeClass + public static void startUp() throws Exception { + final var context = YangParserTestUtils.parseYang(""" + module list-post { + namespace "list-post"; + prefix lp; + container container { + list list { + key "name address"; + leaf name { + type string; + } + leaf address { + type string; + } + } + } + }"""); + final var schemaService = mock(DOMSchemaService.class); + when(schemaService.getGlobalContext()).thenReturn(context); + final var generator = new OpenApiGeneratorRFC8040(schemaService); + final var uriInfo = DocGenTestHelper.createMockUriInfo("http://localhost/path"); + doc = generator.getApiDeclaration("list-post", null, uriInfo); + assertNotNull(doc); + } + + /** + * Test to verify that we do NOT generate OpenApi example POST request for path ending with list element. + * + *

+ * Assert that for paths ending with container we have examples for all types of requests. + * Assert that for paths ending with list we do NOT have POST example. + */ + @Test + public void testListPostRequest() { + // for container, we have both post (with child as payload) and put (with itself as payload) + final var pathToContainer = "/rests/data/list-post:container"; + assertNotNull(doc.paths().get(pathToContainer).get()); + assertNotNull(doc.paths().get(pathToContainer).post()); + assertNotNull(doc.paths().get(pathToContainer).put()); + assertNotNull(doc.paths().get(pathToContainer).patch()); + assertNotNull(doc.paths().get(pathToContainer).delete()); + + // for list, we cannot make a post request + final var pathToList = "/rests/data/list-post:container/list={name},{address}"; + assertNotNull(doc.paths().get(pathToList).get()); + assertNull(doc.paths().get(pathToList).post()); + assertNotNull(doc.paths().get(pathToList).put()); + assertNotNull(doc.paths().get(pathToList).patch()); + assertNotNull(doc.paths().get(pathToList).delete()); + } +} diff --git a/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/OpenApiGeneratorRFC8040Test.java b/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/OpenApiGeneratorRFC8040Test.java index 1359a1e194..da26b1254b 100644 --- a/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/OpenApiGeneratorRFC8040Test.java +++ b/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/OpenApiGeneratorRFC8040Test.java @@ -94,6 +94,8 @@ public final class OpenApiGeneratorRFC8040Test { "/rests/data/toaster2:lst={lf1}/cont1/cont11", "/rests/data/toaster2:lst={lf1}/cont1/lst11={lf111}", "/rests/data/toaster2:lst={lf1}/lst1={key1},{key2}"); + final List configPathsForPost = List.of("/rests/data/toaster2:lst={lf1}/cont1", + "/rests/data/toaster2:lst={lf1}/cont1/cont11"); final OpenApiObject doc = generator.getApiDeclaration(TOASTER_2, REVISION_DATE, uriInfo); @@ -102,9 +104,13 @@ public final class OpenApiGeneratorRFC8040Test { assertNotNull(node.get()); assertNotNull(node.put()); assertNotNull(node.delete()); - assertNotNull(node.post()); assertNotNull(node.patch()); } + + for (final String path : configPathsForPost) { + final Path node = doc.paths().get(path); + assertNotNull(node.post()); + } } /** @@ -228,14 +234,16 @@ public final class OpenApiGeneratorRFC8040Test { assertNotNull(delete); assertEquals(expectedSize, delete.parameters().size()); - final var post = path.post(); - assertNotNull(post); - assertEquals(expectedSize, post.parameters().size()); - final var patch = path.patch(); assertNotNull(patch); assertEquals(expectedSize, patch.parameters().size()); } + + // we do not generate POST for lists + final var path = paths.get("/rests/data/recursive:container-root"); + final var post = path.post(); + final int expectedSize = configPaths.get("/rests/data/recursive:container-root"); + assertEquals(expectedSize, post.parameters().size()); } /** @@ -313,8 +321,6 @@ public final class OpenApiGeneratorRFC8040Test { verifyRequestRef(jsonNodeToaster.get(), "#/components/schemas/toaster2_toaster", CONTAINER); final var jsonNodeToasterSlot = doc.paths().get("/rests/data/toaster2:toaster/toasterSlot={slotId}"); - verifyRequestRef(jsonNodeToasterSlot.post(), "#/components/schemas/toaster2_toaster_toasterSlot_slotInfo", - CONTAINER); verifyRequestRef(jsonNodeToasterSlot.put(), "#/components/schemas/toaster2_toaster_toasterSlot", LIST); verifyRequestRef(jsonNodeToasterSlot.get(), "#/components/schemas/toaster2_toaster_toasterSlot", LIST); @@ -328,13 +334,10 @@ public final class OpenApiGeneratorRFC8040Test { CONTAINER); final var jsonNodeLst = doc.paths().get("/rests/data/toaster2:lst={lf1}"); - verifyRequestRef(jsonNodeLst.post(), "#/components/schemas/toaster2_lst_cont1", CONTAINER); verifyRequestRef(jsonNodeLst.put(), "#/components/schemas/toaster2_lst", LIST); verifyRequestRef(jsonNodeLst.get(), "#/components/schemas/toaster2_lst", LIST); final var jsonNodeLst1 = doc.paths().get("/rests/data/toaster2:lst={lf1}/lst1={key1},{key2}"); - verifyPostDataRequestRef(jsonNodeLst1.post(), "#/components/schemas/toaster2_lst_lst1", - "#/components/schemas/toaster2_lst_lst1"); verifyRequestRef(jsonNodeLst1.put(), "#/components/schemas/toaster2_lst_lst1", LIST); verifyRequestRef(jsonNodeLst1.get(), "#/components/schemas/toaster2_lst_lst1", LIST); diff --git a/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/PostPayloadTest.java b/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/PostPayloadTest.java index 63198a8aab..feced2b90a 100644 --- a/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/PostPayloadTest.java +++ b/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/PostPayloadTest.java @@ -140,14 +140,6 @@ public class PostPayloadTest { final var xmlRef2 = getXmlRef(containerDoc, path2); assertEquals("#/components/schemas/container-test_cont_cont1_list4", xmlRef2); - final var path3 = "/rests/data/container-test:cont/cont1/list4={key4}"; - assertTrue(containerDoc.paths().containsKey(path3)); - final var jsonRef3 = getJsonRef(containerDoc, path3); - assertEquals("{\"cont2\":{\"$ref\":\"#/components/schemas/container-test_cont_cont1_list4_cont2\"}}", - jsonRef3); - final var xmlRef3 = getXmlRef(containerDoc, path3); - assertEquals("#/components/schemas/container-test_cont_cont1_list4_cont2", xmlRef3); - final var path4 = "/rests/data/container-test:cont/cont1/list4={key4}/cont2"; assertTrue(containerDoc.paths().containsKey(path4)); final var jsonRef4 = getJsonRef(containerDoc, path4); @@ -166,13 +158,6 @@ public class PostPayloadTest { + "#/components/schemas/list-test_cont_list1\"}}}", jsonRef1); final var xmlRef1 = getXmlRef(listDoc, path1); assertEquals("#/components/schemas/list-test_cont_list1", xmlRef1); - - final var path2 = "/rests/data/list-test:cont/list2={key2}"; - final var jsonRef2 = getJsonRef(listDoc, path2); - assertEquals("{\"list3\":{\"type\":\"array\",\"items\":{\"$ref\":\"" - + "#/components/schemas/list-test_cont_list2_list3\"}}}", jsonRef2); - final var xmlRef2 = getXmlRef(listDoc, path2); - assertEquals("#/components/schemas/list-test_cont_list2_list3", xmlRef2); } private static String getJsonRef(final OpenApiObject openApiObject, final String path) { diff --git a/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/mountpoints/MountPointOpenApiTest.java b/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/mountpoints/MountPointOpenApiTest.java index 7b7d106e7a..2e8eb56aa2 100644 --- a/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/mountpoints/MountPointOpenApiTest.java +++ b/restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/mountpoints/MountPointOpenApiTest.java @@ -161,7 +161,7 @@ public final class MountPointOpenApiTest { } assertEquals("Unexpected GET paths size", 37, getOperations.size()); - assertEquals("Unexpected POST paths size", 43, postOperations.size()); + assertEquals("Unexpected POST paths size", 27, postOperations.size()); assertEquals("Unexpected PUT paths size", 35, putOperations.size()); assertEquals("Unexpected PATCH paths size", 35, patchOperations.size()); assertEquals("Unexpected DELETE paths size", 35, deleteOperations.size()); @@ -205,14 +205,16 @@ public final class MountPointOpenApiTest { assertNotNull(delete); assertEquals(expectedSize, delete.parameters().size()); - final var post = path.post(); - assertNotNull(post); - assertEquals(expectedSize, post.parameters().size()); - final var patch = path.patch(); assertNotNull(patch); assertEquals(expectedSize, patch.parameters().size()); } + + // POST request exists only for containers + final var post = paths.get("/rests/data/nodes/node=123/yang-ext:mount/recursive:container-root").post(); + assertNotNull(post); + final int expectedSize = configPaths.get("/rests/data/nodes/node=123/yang-ext:mount/recursive:container-root"); + assertEquals(expectedSize, post.parameters().size()); } /** -- 2.36.6