Create child data node (part 2): NCMP service + REST 87/120687/4
authorRuslan Kashapov <ruslan.kashapov@pantheon.tech>
Tue, 20 Apr 2021 08:14:30 +0000 (11:14 +0300)
committerRishi Chail <rishi.chail@est.tech>
Wed, 21 Apr 2021 13:01:53 +0000 (13:01 +0000)
Issue-ID: CPS-337
Change-Id: Icf703f6f375e5f280058d58c781eac081f3dd161
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
cps-ncmp-rest/docs/openapi/ncmproxy.yml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/test/groovy/org/onap/cps/api/impl/NetworkCmProxyDataServiceImplSpec.groovy

index 2a70d70..1bbc096 100644 (file)
@@ -1,5 +1,6 @@
 #  ============LICENSE_START=======================================================
 #  Modification (C) 2021 Nordix Foundation
+#  Modification (C) 2021 Pantheon.tech
 #  ================================================================================
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -64,6 +65,33 @@ nodesByCmHandleAndCpsPath:
         $ref: 'components.yaml#/components/responses/NotFound'
 
 nodesByCmHandleAndXpath:
+  post:
+    description: Create a node with descendants for the given CM Handle; top level or under existing node (requires xpath)
+    tags:
+      - network-cm-proxy
+    summary: Create a node with descendants
+    operationId: createNode
+    parameters:
+      - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+      - $ref: 'components.yaml#/components/parameters/xpathInQuery'
+    requestBody:
+      required: true
+      content:
+        application/json:
+          schema:
+            type: string
+    responses:
+      201:
+        $ref: 'components.yaml#/components/responses/Created'
+      400:
+        $ref: 'components.yaml#/components/responses/BadRequest'
+      401:
+        $ref: 'components.yaml#/components/responses/Unauthorized'
+      403:
+        $ref: 'components.yaml#/components/responses/Forbidden'
+      404:
+        $ref: 'components.yaml#/components/responses/NotFound'
+
   patch:
     description: Update node leaves for the given cps path and cm Handle
     tags:
index acbbdd9..fccfb71 100644 (file)
@@ -47,6 +47,13 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
     @Autowired
     private NetworkCmProxyDataService networkCmProxyDataService;
 
+    @Override
+    public ResponseEntity<String> createNode(final String jsonData, final String cmHandle,
+        final String parentNodeXpath) {
+        networkCmProxyDataService.createDataNode(cmHandle, parentNodeXpath, jsonData);
+        return new ResponseEntity<>(HttpStatus.CREATED);
+    }
+
     @Override
     public ResponseEntity<Object> getNodeByCmHandleAndXpath(final String cmHandle, @Valid final String xpath,
         @Valid final Boolean includeDescendants) {
index aa9fa86..f0a3d09 100644 (file)
 
 package org.onap.cps.ncmp.rest.controller
 
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
 
 import com.google.gson.Gson
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
@@ -35,10 +41,6 @@ import org.springframework.test.web.servlet.MockMvc
 import spock.lang.Specification
 import spock.lang.Unroll
 
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
-
 @WebMvcTest
 class NetworkCmProxyControllerSpec extends Specification {
 
@@ -78,10 +80,32 @@ class NetworkCmProxyControllerSpec extends Specification {
             def expectedJsonContent = new Gson().toJson(dataNode)
             response.getContentAsString().contains(expectedJsonContent)
         where: 'the following options for include descendants are provided in the request'
-            scenario                   | includeDescendantsOption || expectedCpsDataServiceOption
-            'no descendants by default'| ''                       || OMIT_DESCENDANTS
-            'no descendant explicitly' | 'false'                  || OMIT_DESCENDANTS
-            'descendants'              | 'true'                   || INCLUDE_ALL_DESCENDANTS
+            scenario                    | includeDescendantsOption || expectedCpsDataServiceOption
+            'no descendants by default' | ''                       || OMIT_DESCENDANTS
+            'no descendant explicitly'  | 'false'                  || OMIT_DESCENDANTS
+            'descendants'               | 'true'                   || INCLUDE_ALL_DESCENDANTS
+    }
+
+    @Unroll
+    def 'Create data node: #scenario.'() {
+        given: 'json data'
+            def jsonData = 'json data'
+        when: 'post request is performed'
+            def response = mvc.perform(
+                    post("$dataNodeBaseEndpoint/cm-handles/$cmHandle/nodes")
+                            .contentType(MediaType.APPLICATION_JSON)
+                            .content(jsonData)
+                            .param('xpath', reqXpath)
+            ).andReturn().response
+        then: 'the service method is invoked once with expected parameters'
+            1 * mockNetworkCmProxyDataService.createDataNode(cmHandle, usedXpath, jsonData)
+        and: 'response status indicates success'
+            response.status == HttpStatus.CREATED.value()
+        where: 'following parameters were used'
+            scenario             | reqXpath || usedXpath
+            'no xpath parameter' | ''       || '/'
+            'root xpath'         | '/'      || '/'
+            'parent node xpath'  | '/xpath' || '/xpath'
     }
 
     def 'Update data node leaves.'() {
index 158f20e..8176ea5 100644 (file)
@@ -2,6 +2,7 @@
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2021 highstreet technologies GmbH
  *  Copyright (C) 2021 Nordix Foundation
+ *  Modifications Copyright (C) 2021 Pantheon.tech
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -56,6 +57,16 @@ public interface NetworkCmProxyDataService {
     Collection<DataNode> queryDataNodes(@NonNull String cmHandle, @NonNull String cpsPath,
         @NonNull FetchDescendantsOption fetchDescendantsOption);
 
+    /**
+     * Creates data node with descendants at root level or under existing node (if parent node xpath is provided).
+     *
+     * @param cmHandle        The identifier for a network function, network element, subnetwork or any other cm
+     *                        object managed by Network CM Proxy
+     * @param parentNodeXpath xpath to parent node or '/' for root level
+     * @param jsonData        data as JSON string
+     */
+    void createDataNode(@NonNull String cmHandle, @NonNull String parentNodeXpath, @NonNull String jsonData);
+
     /**
      * Updates data node for given cm handle using xpath to parent node.
      *
index 9e01314..56f4cf8 100755 (executable)
@@ -2,6 +2,7 @@
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2021 highstreet technologies GmbH
  *  Copyright (C) 2021 Nordix Foundation
+ *  Modifications Copyright (C) 2021 Pantheon.tech
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -28,6 +29,7 @@ import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
 
 @Service
 public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService {
@@ -40,25 +42,38 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     @Autowired
     private CpsQueryService cpsQueryService;
 
+    private String getDataspaceName() {
+        return NF_PROXY_DATASPACE_NAME;
+    }
+
     @Override
     public DataNode getDataNode(final String cmHandle, final String xpath,
         final FetchDescendantsOption fetchDescendantsOption) {
-        return cpsDataService.getDataNode(NF_PROXY_DATASPACE_NAME, cmHandle, xpath, fetchDescendantsOption);
+        return cpsDataService.getDataNode(getDataspaceName(), cmHandle, xpath, fetchDescendantsOption);
     }
 
     @Override
     public Collection<DataNode> queryDataNodes(final String cmHandle, final String cpsPath,
         final FetchDescendantsOption fetchDescendantsOption) {
-        return cpsQueryService.queryDataNodes(NF_PROXY_DATASPACE_NAME, cmHandle, cpsPath, fetchDescendantsOption);
+        return cpsQueryService.queryDataNodes(getDataspaceName(), cmHandle, cpsPath, fetchDescendantsOption);
+    }
+
+    @Override
+    public void createDataNode(final String cmHandle, final String parentNodeXpath, final String jsonData) {
+        if (StringUtils.isEmpty(parentNodeXpath) || "/".equals(parentNodeXpath)) {
+            cpsDataService.saveData(getDataspaceName(), cmHandle, jsonData);
+        } else {
+            cpsDataService.saveData(getDataspaceName(), cmHandle, parentNodeXpath, jsonData);
+        }
     }
 
     @Override
     public void updateNodeLeaves(final String cmHandle, final String parentNodeXpath, final String jsonData) {
-        cpsDataService.updateNodeLeaves(NF_PROXY_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData);
+        cpsDataService.updateNodeLeaves(getDataspaceName(), cmHandle, parentNodeXpath, jsonData);
     }
 
     @Override
     public void replaceNodeTree(final String cmHandle, final String parentNodeXpath, final String jsonData) {
-        cpsDataService.replaceNodeTree(NF_PROXY_DATASPACE_NAME, cmHandle, parentNodeXpath, jsonData);
+        cpsDataService.replaceNodeTree(getDataspaceName(), cmHandle, parentNodeXpath, jsonData);
     }
 }
index 49028be..95493bf 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Nordix Foundation
+ *  Modifications Copyright (C) 2021 Pantheon.tech
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -24,6 +25,7 @@ import org.onap.cps.api.CpsQueryService
 import org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServiceImpl
 import org.onap.cps.spi.FetchDescendantsOption
 import spock.lang.Specification
+import spock.lang.Unroll
 
 class NetworkCmProxyDataServiceImplSpec extends Specification {
     def objectUnderTest = new NetworkCmProxyDataServiceImpl()
@@ -49,6 +51,30 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             fetchDescendantsOption << FetchDescendantsOption.values()
     }
 
+    @Unroll
+    def 'Create full data node: #scenario.'() {
+        given: 'a cm handle and root xpath'
+            def jsonData = 'some json'
+        when: 'createDataNode is invoked'
+            objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
+        then: 'the CPS service method is invoked once with the expected parameters'
+            1 * mockcpsDataService.saveData(expectedDataspaceName, cmHandle, jsonData)
+        where: 'following parameters were used'
+            scenario           | xpath
+            'no xpath'         | ''
+            'root level xpath' | '/'
+    }
+
+    def 'Create child data node.'() {
+        given: 'a cm handle and parent node xpath'
+            def jsonData = 'some json'
+            def xpath = '/test-node'
+        when: 'createDataNode is invoked'
+            objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
+        then: 'the CPS service method is invoked once with the expected parameters'
+            1 * mockcpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData)
+    }
+
     def 'Update data node leaves.'() {
         given: 'a cm Handle and a cps path'
             def xpath = '/xpath'