final ObjectNode post = JsonNodeFactory.instance.objectNode();
final String moduleName = module.getName();
final String name = moduleName + MODULE_NAME_SUFFIX;
- post.set("post", buildPost("", name, "", moduleName, deviceName,
+ post.set("post", buildPost(null, "", name, "", moduleName, deviceName,
module.getDescription().orElse(""), pathParams, oaversion));
paths.set(resourcePath, post);
}
final ObjectNode delete = buildDelete(node, moduleName, deviceName, pathParams, oaversion);
operations.put("delete", delete);
- operations.put("post", buildPost(parentName, nodeName, discriminator, moduleName, deviceName,
+ operations.put("post", buildPost(node, parentName, nodeName, discriminator, moduleName, deviceName,
node.getDescription().orElse(""), pathParams, oaversion));
}
return operations;
import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
import org.opendaylight.netconf.sal.rest.doc.impl.DefinitionNames;
import org.opendaylight.netconf.sal.rest.doc.util.JsonUtil;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
}
- public static ObjectNode buildPost(final String parentName, final String nodeName, final String discriminator,
- final String moduleName, final Optional<String> deviceName,
- final String description, final ArrayNode pathParams,
- final OAversion oaversion) {
+ public static ObjectNode buildPost(final DataSchemaNode node, final String parentName, final String nodeName,
+ final String discriminator, final String moduleName,
+ final Optional<String> deviceName, final String description,
+ final ArrayNode pathParams, final OAversion oaversion) {
final ObjectNode value = JsonNodeFactory.instance.objectNode();
value.put(DESCRIPTION_KEY, description);
value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.POST, moduleName, deviceName, nodeName));
final String defName = cleanDefName + discriminator;
final String xmlDefName = cleanDefName + XML_SUFFIX + discriminator;
ref.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
- insertRequestBodyParameter(parameters, value, defName, xmlDefName, nodeName + CONFIG, oaversion);
+ final DataSchemaNode childNode = getListOrContainerChildNode(Optional.ofNullable(node));
+ if (childNode != null && childNode.isConfiguration()) {
+ final String childNodeName = childNode.getQName().getLocalName();
+ final String cleanChildDefName = parentName + "_" + nodeName + CONFIG + "_" + childNodeName + POST_SUFFIX;
+ final String childDefName = cleanChildDefName + discriminator;
+ final String childXmlDefName = cleanChildDefName + XML_SUFFIX + discriminator;
+ insertPostRequestBodyParameter(childNode, parameters, value, childDefName, childXmlDefName, childNodeName,
+ oaversion);
+ } else {
+ insertRequestBodyParameter(parameters, value, defName, xmlDefName, nodeName + CONFIG, oaversion);
+ }
value.set(PARAMETERS_KEY, parameters);
final ObjectNode responses = JsonNodeFactory.instance.objectNode();
}
}
+ private static void insertPostRequestBodyParameter(final DataSchemaNode childNode, final ArrayNode parameters,
+ final ObjectNode operation, final String defName, final String xmlDefName, final String name,
+ final OAversion oaversion) {
+ final ObjectNode payload = JsonNodeFactory.instance.objectNode();
+ if (oaversion.equals(OAversion.V3_0)) {
+ final ObjectNode content = JsonNodeFactory.instance.objectNode();
+ final ObjectNode properties = JsonNodeFactory.instance.objectNode();
+ if (childNode instanceof ListSchemaNode) {
+ final ObjectNode list = JsonNodeFactory.instance.objectNode();
+ final ObjectNode listValue = JsonNodeFactory.instance.objectNode();
+ listValue.put(TYPE_KEY, "array");
+ listValue.set("items", buildRefSchema(defName, oaversion));
+ list.set(name, listValue);
+ properties.set(PROPERTIES_KEY, list);
+ } else {
+ final ObjectNode container = JsonNodeFactory.instance.objectNode();
+ container.set(name, buildRefSchema(defName, oaversion));
+ properties.set(PROPERTIES_KEY, container);
+ }
+ final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
+ jsonSchema.set(SCHEMA_KEY, properties);
+ content.set(MediaType.APPLICATION_JSON, jsonSchema);
+ content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
+ payload.set(CONTENT_KEY, content);
+ payload.put(DESCRIPTION_KEY, name);
+ operation.set(REQUEST_BODY_KEY, payload);
+ } else {
+ payload.put(IN_KEY, BODY);
+ payload.put(NAME_KEY, name);
+ payload.set(SCHEMA_KEY, buildRefSchema(defName, OAversion.V2_0));
+ parameters.add(payload);
+ }
+ }
+
private static ObjectNode buildRefSchema(final String defName, final OAversion oaversion) {
final ObjectNode schema = JsonNodeFactory.instance.objectNode();
schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
}
return parameter;
}
+
+ private static DataSchemaNode getListOrContainerChildNode(final Optional<DataSchemaNode> node) {
+ return node.flatMap(schemaNode -> ((DataNodeContainer) schemaNode).getChildNodes().stream()
+ .filter(n -> n instanceof ListSchemaNode || n instanceof ContainerSchemaNode)
+ .findFirst()).orElse(null);
+ }
}
private static final String MANDATORY_TEST_MODULE = "mandatory-test_module";
private static final String CHOICE_TEST_MODULE = "choice-test";
private static final String PROPERTIES = "properties";
+ private static final String CONTAINER = "container";
+ private static final String LIST = "list";
private final ApiDocGeneratorRFC8040 generator = new ApiDocGeneratorRFC8040(SCHEMA_SERVICE);
final var doc = (OpenApiObject) generator.getApiDeclaration(NAME, REVISION_DATE, URI_INFO,
ApiDocServiceImpl.OAversion.V3_0);
final var jsonNodeToaster = doc.getPaths().get("/rests/data/toaster2:toaster");
- verifyRequestRef(jsonNodeToaster.path("post"), "#/components/schemas/toaster2_config_toaster_post",
- "#/components/schemas/toaster2_config_toaster_post_xml");
+ verifyPostRequestRef(jsonNodeToaster.path("post"),
+ "#/components/schemas/toaster2_toaster_config_toasterSlot_post",
+ "#/components/schemas/toaster2_toaster_config_toasterSlot_post_xml", LIST);
verifyRequestRef(jsonNodeToaster.path("put"), "#/components/schemas/toaster2_config_toaster_TOP",
"#/components/schemas/toaster2_config_toaster");
verifyRequestRef(jsonNodeToaster.path("get"), "#/components/schemas/toaster2_toaster_TOP",
"#/components/schemas/toaster2_toaster");
final var jsonNodeToasterSlot = doc.getPaths().get("/rests/data/toaster2:toaster/toasterSlot={slotId}");
- verifyRequestRef(jsonNodeToasterSlot.path("post"),
- "#/components/schemas/toaster2_toaster_config_toasterSlot_post",
- "#/components/schemas/toaster2_toaster_config_toasterSlot_post_xml");
+ verifyPostRequestRef(jsonNodeToasterSlot.path("post"),
+ "#/components/schemas/toaster2_toaster_toasterSlot_config_slotInfo_post",
+ "#/components/schemas/toaster2_toaster_toasterSlot_config_slotInfo_post_xml", CONTAINER);
verifyRequestRef(jsonNodeToasterSlot.path("put"),
"#/components/schemas/toaster2_toaster_config_toasterSlot_TOP",
"#/components/schemas/toaster2_toaster_config_toasterSlot");
"#/components/schemas/toaster2_toaster_toasterSlot_slotInfo");
final var jsonNodeLst = doc.getPaths().get("/rests/data/toaster2:lst");
- verifyRequestRef(jsonNodeLst.path("post"), "#/components/schemas/toaster2_config_lst_post",
- "#/components/schemas/toaster2_config_lst_post_xml");
+ verifyPostRequestRef(jsonNodeLst.path("post"), "#/components/schemas/toaster2_lst_config_cont1_post",
+ "#/components/schemas/toaster2_lst_config_cont1_post_xml", CONTAINER);
verifyRequestRef(jsonNodeLst.path("put"), "#/components/schemas/toaster2_config_lst_TOP",
"#/components/schemas/toaster2_config_lst");
verifyRequestRef(jsonNodeLst.path("get"), "#/components/schemas/toaster2_lst_TOP",
assertNotNull(postXmlRef);
assertEquals(expectedXmlRef, postXmlRef.textValue());
}
+
+ private static void verifyPostRequestRef(final JsonNode path, final String expectedJsonRef,
+ final String expectedXmlRef, String nodeType) {
+ final JsonNode postContent;
+ if (path.get("requestBody") != null) {
+ postContent = path.get("requestBody").get("content");
+ } else {
+ postContent = path.get("responses").get("200").get("content");
+ }
+ assertNotNull(postContent);
+ final String postJsonRef;
+ if (nodeType.equals(CONTAINER)) {
+ postJsonRef = postContent.path("application/json").path("schema").path("properties").elements().next()
+ .path("$ref").textValue();
+ } else {
+ postJsonRef = postContent.path("application/json").path("schema").path("properties").elements().next()
+ .path("items").path("$ref").textValue();
+ }
+ assertEquals(expectedJsonRef, postJsonRef);
+ final var postXmlRef = postContent.get("application/xml").get("schema").get("$ref");
+ assertNotNull(postXmlRef);
+ assertEquals(expectedXmlRef, postXmlRef.textValue());
+ }
}
--- /dev/null
+/*
+ * 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.netconf.sal.rest.doc.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
+import org.opendaylight.netconf.sal.rest.doc.swagger.OpenApiObject;
+
+/**
+ * These tests are designed to verify that the specified path contains corresponding references in post requests
+ * which contains lists and containers.
+ *
+ * <p>
+ * The purpose of this test is to ensure that the specified path in the document contains the necessary references
+ * in its post requests for both JSON and XML content types. It verifies that the expected structure is in place,
+ * and the application can successfully retrieve the corresponding references for further processing.
+ */
+public class PostPayloadTest extends AbstractApiDocTest {
+ private static final String CONTENT_KEY = "content";
+ private static final String SCHEMA_KEY = "schema";
+
+ private static OpenApiObject containerDoc;
+ private static OpenApiObject listDoc;
+
+ @BeforeClass
+ public static void startUp() {
+ final var generator = new ApiDocGeneratorRFC8040(SCHEMA_SERVICE);
+ containerDoc = (OpenApiObject) generator.getApiDeclaration("container-test", "2023-07-31", URI_INFO,
+ OAversion.V3_0);
+ assertNotNull(containerDoc);
+ listDoc = (OpenApiObject) generator.getApiDeclaration("list-test", "2023-07-31", URI_INFO, OAversion.V3_0);
+ assertNotNull(listDoc);
+ }
+
+ @Test
+ public void testContainersPostPayloads() {
+ final var path1 = "/rests/data/container-test:cont";
+ assertNotNull(containerDoc.getPaths().get(path1));
+ final var jsonRef1 = getJsonRef(containerDoc, path1);
+ assertEquals("{\"cont1\":{\"$ref\":\"#/components/schemas/container-test_cont_config_cont1_post\"}}",
+ jsonRef1);
+ final var xmlRef1 = getXmlRef(containerDoc, path1);
+ assertEquals("#/components/schemas/container-test_cont_config_cont1_post_xml", xmlRef1);
+
+ final var path2 = "/rests/data/container-test:cont/cont1";
+ assertNotNull(containerDoc.getPaths().get(path2));
+ final var jsonRef2 = getJsonRef(containerDoc, path2);
+ assertEquals("{\"list4\":{\"type\":\"array\",\"items\":{\"$ref\":\""
+ + "#/components/schemas/container-test_cont_cont1_config_list4_post\"}}}", jsonRef2);
+ final var xmlRef2 = getXmlRef(containerDoc, path2);
+ assertEquals("#/components/schemas/container-test_cont_cont1_config_list4_post_xml", xmlRef2);
+
+ final var path3 = "/rests/data/container-test:cont/cont1/list4={key4}";
+ assertNotNull(containerDoc.getPaths().get(path3));
+ final var jsonRef3 = getJsonRef(containerDoc, path3);
+ assertEquals("{\"cont2\":{\"$ref\":\"#/components/schemas/"
+ + "container-test_cont_cont1_list4_config_cont2_post\"}}", jsonRef3);
+ final var xmlRef3 = getXmlRef(containerDoc, path3);
+ assertEquals("#/components/schemas/container-test_cont_cont1_list4_config_cont2_post_xml", xmlRef3);
+
+ final var path4 = "/rests/data/container-test:cont/cont1/list4={key4}/cont2";
+ assertNotNull(containerDoc.getPaths().get(path4));
+ final var jsonRef4 = getJsonRef(containerDoc, path4);
+ assertEquals("{\"list5\":{\"type\":\"array\",\"items\":{\"$ref\":\""
+ + "#/components/schemas/container-test_cont_cont1_list4_cont2_config_list5_post\"}}}", jsonRef4);
+ final var xmlRef4 = getXmlRef(containerDoc, path4);
+ assertEquals("#/components/schemas/container-test_cont_cont1_list4_cont2_config_list5_post_xml", xmlRef4);
+ }
+
+ @Test
+ public void testListsPostPayloads() {
+ final var path1 = "/rests/data/list-test:cont";
+ assertNotNull(listDoc.getPaths().get(path1));
+ final var jsonRef1 = getJsonRef(listDoc, path1);
+ assertEquals("{\"list1\":{\"type\":\"array\",\"items\":{\"$ref\":\""
+ + "#/components/schemas/list-test_cont_config_list1_post\"}}}", jsonRef1);
+ final var xmlRef1 = getXmlRef(listDoc, path1);
+ assertEquals("#/components/schemas/list-test_cont_config_list1_post_xml", xmlRef1);
+
+ final var path2 = "/rests/data/list-test:cont/list2={key2}";
+ assertNotNull(listDoc.getPaths().get(path2));
+ final var jsonRef2 = getJsonRef(listDoc, path2);
+ assertEquals("{\"list3\":{\"type\":\"array\",\"items\":{\"$ref\":\""
+ + "#/components/schemas/list-test_cont_list2_config_list3_post\"}}}", jsonRef2);
+ final var xmlRef2 = getXmlRef(listDoc, path2);
+ assertEquals("#/components/schemas/list-test_cont_list2_config_list3_post_xml", xmlRef2);
+ }
+
+ private static String getJsonRef(final OpenApiObject openApiObject, final String path) {
+ return openApiObject.getPaths().get(path).get("post").get("requestBody").get(CONTENT_KEY)
+ .get("application/json").get(SCHEMA_KEY).get("properties").toString();
+ }
+
+ private static String getXmlRef(final OpenApiObject openApiObject, final String path) {
+ return openApiObject.getPaths().get(path).get("post").get("requestBody").get(CONTENT_KEY)
+ .get("application/xml").get(SCHEMA_KEY).get("$ref").asText();
+ }
+}
--- /dev/null
+module container-test {
+ namespace "urn:opendaylight:params:xml:ns:yang:netconf:monitoring:cont-test";
+ prefix "ctest";
+ revision 2023-07-31 {
+ description "Test model.";
+ }
+
+ container cont {
+ container cont1 {
+ list list4 {
+ key "key4";
+ leaf key4 {
+ type string;
+ }
+ leaf value4 {
+ type string;
+ }
+ container cont2 {
+ leaf value7 {
+ type string;
+ }
+ list list5 {
+ key "key5";
+ leaf key5 {
+ type string;
+ }
+ leaf value5 {
+ type string;
+ }
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+module list-test {
+ namespace "urn:opendaylight:params:xml:ns:yang:netconf:monitoring:list-test";
+ prefix "ltest";
+ revision 2023-07-31 {
+ description "Test model.";
+ }
+ container cont {
+ list list1 {
+ key "key1";
+ leaf key1 {
+ type string;
+ }
+ leaf value1 {
+ type string;
+ }
+ }
+ list list2 {
+ key "key2";
+ leaf key2 {
+ type string;
+ }
+ leaf value2 {
+ type string;
+ }
+ list list3 {
+ key "key3";
+ leaf key3 {
+ type string;
+ }
+ leaf value3 {
+ type string;
+ }
+ }
+ }
+ }
+}