Implement DMI Registration (NCMP-Side) 18/122418/22
authorDylanB95EST <dylan.byrne@est.tech>
Fri, 2 Jul 2021 12:30:42 +0000 (13:30 +0100)
committerDylanB95EST <dylan.byrne@est.tech>
Tue, 3 Aug 2021 15:47:24 +0000 (16:47 +0100)
Implementing registration of CM-Handles to NCMP from DMI

CM Handles are Written to fragment tables

Moved NetworkCmProxyDataServiceImplSpec.groovy as it was not in the same
package as the class it was testing. Meaning it didn't cover this when
it came to code coverage

Have included Json structure validation also within open api

Deprecating old API's along with old API Methods

Issue-ID: CPS-442

Change-Id: I819b9bf65280b1d968d3b75ca5ef2f9eb5617579
Signed-off-by: DylanB95EST <dylan.byrne@est.tech>
22 files changed:
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmproxy.yml
cps-ncmp-rest/docs/openapi/openapi.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-rest/src/test/java/org/onap/cps/TestUtils.java [new file with mode: 0644]
cps-ncmp-rest/src/test/resources/dmi-registration.json [new file with mode: 0644]
cps-ncmp-service/pom.xml
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/main/java/org/onap/cps/ncmp/api/models/CmHandle.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandle.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy [moved from cps-ncmp-service/src/test/groovy/org/onap/cps/api/impl/NetworkCmProxyDataServiceImplSpec.groovy with 69% similarity]
cps-ncmp-service/src/test/resources/application.yml [new file with mode: 0644]
cps-parent/pom.xml
cps-ri/src/main/resources/changelog/changelog-master.yaml
cps-ri/src/main/resources/changelog/db/changes/09-loadData-dmi-registry-schema-set.yaml
cps-ri/src/main/resources/changelog/db/changes/10-loadData-dmi-registry-fragment.yaml [new file with mode: 0644]
cps-ri/src/main/resources/changelog/db/changes/data/dmi/fragment.csv [new file with mode: 0644]
spotbugs/src/main/resources/spotbugs-exclude.xml

index 26d55bf..9921041 100644 (file)
@@ -29,14 +29,33 @@ components:
           type: string
         details:
           type: string
-    MultipartFile:
+
+    RestDmiPluginRegistration:
+      type: object
+      properties:
+        dmiPlugin:
+          type: string
+          example: onap-dmi-plugin
+        createdCmHandles:
+          type: array
+          items:
+            $ref: '#/components/schemas/RestCmHandle'
+
+    RestCmHandle:
       required:
-        - file
+        - cmHandle
+      type: object
       properties:
-        multipartFile:
+        cmHandle:
           type: string
-          description: multipartFile
-          format: binary
+          example: cmHandle123
+        cmHandleProperties:
+            $ref: '#/components/schemas/RestCmHandleAdditionalProperties'
+    RestCmHandleAdditionalProperties:
+        type: object
+        additionalProperties:
+            type: string
+            example: system-001
 
   parameters:
     cmHandleInPath:
@@ -124,10 +143,7 @@ components:
             type: object
     Created:
       description: Created
-      content:
-        text/plain:
-          schema:
-            type: string
+      content: {}
     NoContent:
       description: No Content
       content: {}
index 6ed4f02..3ec7bfd 100755 (executable)
@@ -20,6 +20,7 @@
 nodeByCmHandleAndXpath:
   get:
     description: Get a node with an option to retrieve all the children for a given cm Handle
+    deprecated: true
     tags:
       - network-cm-proxy
     summary: Get a node given a cm Handle and xpath
@@ -43,6 +44,7 @@ nodeByCmHandleAndXpath:
 nodesByCmHandleAndCpsPath:
   get:
     description: Query nodes for the given cps path and cm Handle
+    deprecated: true
     tags:
       - network-cm-proxy
     summary: Query data nodes
@@ -66,6 +68,7 @@ nodesByCmHandleAndCpsPath:
 nodesByCmHandleAndXpath:
   post:
     description: Create a node with descendants for the given CM Handle; top level or under existing node (requires xpath)
+    deprecated: true
     tags:
       - network-cm-proxy
     summary: Create a node with descendants
@@ -93,6 +96,7 @@ nodesByCmHandleAndXpath:
 
   patch:
     description: Update node leaves for the given cps path and cm Handle
+    deprecated: true
     tags:
       - network-cm-proxy
     summary: Update node leaves
@@ -120,6 +124,7 @@ nodesByCmHandleAndXpath:
 
   put:
     description: Replace a node with descendants for the given cps path and cm Handle
+    deprecated: true
     tags:
       - network-cm-proxy
     summary: Replace a node with descendants
@@ -148,6 +153,7 @@ nodesByCmHandleAndXpath:
 listNodeByCmHandleAndXpath:
   post:
     description: Add one or more list-node child elements under existing node for the given CM Handle
+    deprecated: true
     tags:
       - network-cm-proxy
     summary: Add list-node child element(s)
@@ -172,3 +178,26 @@ listNodeByCmHandleAndXpath:
         $ref: 'components.yaml#/components/responses/Forbidden'
       404:
         $ref: 'components.yaml#/components/responses/NotFound'
+
+updateDmiRegistration:
+  post:
+    description: Register a DMI Plugin with any new CM Handles.
+    tags:
+      - network-cm-proxy
+    summary: DMI notifies NCMP of new CM Handles
+    operationId: updateDmiPluginRegistration
+    requestBody:
+      required: true
+      content:
+        application/json:
+          schema:
+            $ref: 'components.yaml#/components/schemas/RestDmiPluginRegistration'
+    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'
\ No newline at end of file
index c77f9a7..64de922 100755 (executable)
@@ -36,3 +36,6 @@ paths:
 
   /v1/cm-handles/{cm-handle}/nodes:
     $ref: 'ncmproxy.yml#/nodesByCmHandleAndXpath'
+
+  /ncmp-dmi/v1/ch:
+    $ref: 'ncmproxy.yml#/updateDmiRegistration'
\ No newline at end of file
index 484a21a..e9e0223 100755 (executable)
 
 package org.onap.cps.ncmp.rest.controller;
 
+
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import java.util.Collection;
 import javax.validation.Valid;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
 import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
+import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.utils.DataMapUtils;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-
 @RestController
 @RequestMapping("${rest.api.ncmp-base-path}")
 public class NetworkCmProxyController implements NetworkCmProxyApi {
 
     private static final Gson GSON = new GsonBuilder().create();
 
-    @Autowired
-    private NetworkCmProxyDataService networkCmProxyDataService;
+    private final NetworkCmProxyDataService networkCmProxyDataService;
 
+    private final ObjectMapper objectMapper;
+
+    /**
+     * Constructor Injection for Dependencies.
+     * @param networkCmProxyDataService Data Service Interface
+     * @param objectMapper Object Mapper
+     */
+    public NetworkCmProxyController(final NetworkCmProxyDataService networkCmProxyDataService,
+        final ObjectMapper objectMapper) {
+        this.networkCmProxyDataService = networkCmProxyDataService;
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Create Node.
+     * @deprecated This Method is no longer used as part of NCMP.
+     */
     @Override
-    public ResponseEntity<String> createNode(final String jsonData, final String cmHandle,
+    @Deprecated(forRemoval = false)
+    public ResponseEntity<Void> createNode(final String jsonData, final String cmHandle,
         final String parentNodeXpath) {
         networkCmProxyDataService.createDataNode(cmHandle, parentNodeXpath, jsonData);
         return new ResponseEntity<>(HttpStatus.CREATED);
     }
 
+    /**
+     * Add List-node Child Element.
+     * @deprecated This Method is no longer used as part of NCMP.
+     */
     @Override
-    public ResponseEntity<String> addListNodeElements(final String jsonData, final String parentNodeXpath,
+    @Deprecated(forRemoval = false)
+    public ResponseEntity<Void> addListNodeElements(final String jsonData, final String parentNodeXpath,
         final String cmHandle) {
         networkCmProxyDataService.addListNodeElements(cmHandle, parentNodeXpath, jsonData);
         return new ResponseEntity<>(HttpStatus.CREATED);
     }
 
+    /**
+     * Get Node By CM Handle and X-Path.
+     * @deprecated This Method is no longer used as part of NCMP.
+     */
     @Override
+    @Deprecated(forRemoval = false)
     public ResponseEntity<Object> getNodeByCmHandleAndXpath(final String cmHandle, @Valid final String xpath,
         @Valid final Boolean includeDescendants) {
         final FetchDescendantsOption fetchDescendantsOption = Boolean.TRUE.equals(includeDescendants)
@@ -69,7 +98,25 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
         return new ResponseEntity<>(DataMapUtils.toDataMap(dataNode), HttpStatus.OK);
     }
 
+    /**
+     * Update DMI Plugin Registration (used for first registration also).
+     * @param restDmiPluginRegistration the registration data
+     */
     @Override
+    public ResponseEntity<Void> updateDmiPluginRegistration(
+        final @Valid RestDmiPluginRegistration restDmiPluginRegistration) {
+        final DmiPluginRegistration dmiPluginRegistration =
+            convertRestObjectToJavaApiObject(restDmiPluginRegistration);
+        networkCmProxyDataService.updateDmiPluginRegistration(dmiPluginRegistration);
+        return new ResponseEntity<>(HttpStatus.CREATED);
+    }
+
+    /**
+     * Query Data Nodes.
+     * @deprecated This Method is no longer used as part of NCMP.
+     */
+    @Override
+    @Deprecated(forRemoval = false)
     public ResponseEntity<Object> queryNodesByCmHandleAndCpsPath(final String cmHandle, @Valid final String cpsPath,
         @Valid final Boolean includeDescendants) {
         final FetchDescendantsOption fetchDescendantsOption = Boolean.TRUE.equals(includeDescendants)
@@ -79,17 +126,34 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
         return new ResponseEntity<>(GSON.toJson(dataNodes), HttpStatus.OK);
     }
 
+    /**
+     * Replace Node With Descendants.
+     * @deprecated This Method is no longer used as part of NCMP.
+     */
     @Override
+    @Deprecated(forRemoval = false)
     public ResponseEntity<Object> replaceNode(@Valid final String jsonData, final String cmHandle,
         @Valid final String parentNodeXpath) {
         networkCmProxyDataService.replaceNodeTree(cmHandle, parentNodeXpath, jsonData);
         return new ResponseEntity<>(HttpStatus.OK);
     }
 
+    /**
+     * Update Node Leaves.
+     * @deprecated This Method is no longer used as part of NCMP.
+     */
     @Override
+    @Deprecated(forRemoval = false)
     public ResponseEntity<Object> updateNodeLeaves(@Valid final String jsonData, final String cmHandle,
         @Valid final String parentNodeXpath) {
         networkCmProxyDataService.updateNodeLeaves(cmHandle, parentNodeXpath, jsonData);
         return new ResponseEntity<>(HttpStatus.OK);
     }
+
+
+    private DmiPluginRegistration convertRestObjectToJavaApiObject(
+        final RestDmiPluginRegistration restDmiPluginRegistration) {
+        return objectMapper.convertValue(restDmiPluginRegistration, DmiPluginRegistration.class);
+    }
+
 }
index 45e8f11..f537980 100644 (file)
@@ -28,7 +28,9 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
 
+import com.fasterxml.jackson.databind.ObjectMapper
 import com.google.gson.Gson
+import org.onap.cps.TestUtils
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
 import org.onap.cps.spi.model.DataNodeBuilder
 import org.spockframework.spring.SpringBean
@@ -49,13 +51,19 @@ class NetworkCmProxyControllerSpec extends Specification {
     @SpringBean
     NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
 
+    @SpringBean
+    ObjectMapper objectMapper = new ObjectMapper()
+
     @Value('${rest.api.ncmp-base-path}')
     def basePath
 
-    def dataNodeBaseEndpoint
+    def deprecatedDataNodeBaseEndPoint
+
+    def ncmpDmiEndpoint
 
     def setup() {
-        dataNodeBaseEndpoint = "$basePath/v1"
+        deprecatedDataNodeBaseEndPoint = "$basePath/v1"
+        ncmpDmiEndpoint = "$basePath/ncmp-dmi/v1"
     }
 
     def cmHandle = 'some handle'
@@ -67,7 +75,7 @@ class NetworkCmProxyControllerSpec extends Specification {
             def cpsPath = 'some cps-path'
             mockNetworkCmProxyDataService.queryDataNodes(cmHandle, cpsPath, expectedCpsDataServiceOption) >> [dataNode]
         and: 'the query endpoint'
-            def dataNodeEndpoint = "$dataNodeBaseEndpoint/cm-handles/$cmHandle/nodes/query"
+            def dataNodeEndpoint = "$deprecatedDataNodeBaseEndPoint/cm-handles/$cmHandle/nodes/query"
         when: 'query data nodes API is invoked'
             def response = mvc.perform(get(dataNodeEndpoint)
                     .param('cps-path', cpsPath)
@@ -89,7 +97,7 @@ class NetworkCmProxyControllerSpec extends Specification {
             def jsonData = 'json data'
         when: 'post request is performed'
             def response = mvc.perform(
-                    post("$dataNodeBaseEndpoint/cm-handles/$cmHandle/nodes")
+                    post("$deprecatedDataNodeBaseEndPoint/cm-handles/$cmHandle/nodes")
                             .contentType(MediaType.APPLICATION_JSON)
                             .content(jsonData)
                             .param('xpath', reqXpath)
@@ -111,7 +119,7 @@ class NetworkCmProxyControllerSpec extends Specification {
             def parentNodeXpath = 'parent node xpath'
         when: 'post request is performed'
             def response = mvc.perform(
-                    post("$dataNodeBaseEndpoint/cm-handles/$cmHandle/list-node")
+                    post("$deprecatedDataNodeBaseEndPoint/cm-handles/$cmHandle/list-node")
                             .contentType(MediaType.APPLICATION_JSON)
                             .content(jsonData)
                             .param('xpath', parentNodeXpath)
@@ -126,7 +134,7 @@ class NetworkCmProxyControllerSpec extends Specification {
         given: 'json data'
             def jsonData = 'json data'
         and: 'the query endpoint'
-            def endpoint = "$dataNodeBaseEndpoint/cm-handles/$cmHandle/nodes"
+            def endpoint = "$deprecatedDataNodeBaseEndPoint/cm-handles/$cmHandle/nodes"
         when: 'patch request is performed'
             def response = mvc.perform(
                     patch(endpoint)
@@ -144,7 +152,7 @@ class NetworkCmProxyControllerSpec extends Specification {
         given: 'json data'
             def jsonData = 'json data'
         and: 'the query endpoint'
-            def endpoint = "$dataNodeBaseEndpoint/cm-handles/$cmHandle/nodes"
+            def endpoint = "$deprecatedDataNodeBaseEndPoint/cm-handles/$cmHandle/nodes"
         when: 'put request is performed'
             def response = mvc.perform(
                     put(endpoint)
@@ -164,7 +172,7 @@ class NetworkCmProxyControllerSpec extends Specification {
             def dataNode = new DataNodeBuilder().withXpath(xpath).withLeaves(["leaf": "value"]).build()
             mockNetworkCmProxyDataService.getDataNode(cmHandle, xpath, OMIT_DESCENDANTS) >> dataNode
         and: 'the query endpoint'
-            def endpoint = "$dataNodeBaseEndpoint/cm-handles/$cmHandle/node"
+            def endpoint = "$deprecatedDataNodeBaseEndPoint/cm-handles/$cmHandle/node"
         when: 'get request is performed through REST API'
             def response = mvc.perform(get(endpoint).param('xpath', xpath)).andReturn().response
         then: 'a success response is returned'
@@ -172,5 +180,21 @@ class NetworkCmProxyControllerSpec extends Specification {
         and: 'response contains expected leaf and value'
             response.contentAsString.contains('"leaf":"value"')
     }
+
+    def 'Register CM Handle Event' () {
+        given: 'jsonData'
+            def jsonData = TestUtils.getResourceFileContent('dmi-registration.json')
+        when: 'post request is performed'
+            def response = mvc.perform(
+                post("$ncmpDmiEndpoint/ch")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(jsonData)
+            ).andReturn().response
+        then: 'the cm handles are registered with the service'
+            1 * mockNetworkCmProxyDataService.updateDmiPluginRegistration(_)
+        and: 'response status is created'
+            response.status == HttpStatus.CREATED.value()
+    }
+
 }
 
diff --git a/cps-ncmp-rest/src/test/java/org/onap/cps/TestUtils.java b/cps-ncmp-rest/src/test/java/org/onap/cps/TestUtils.java
new file mode 100644 (file)
index 0000000..4a6f5e5
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+/**
+ * Common convenience methods for testing.
+ */
+public class TestUtils {
+
+    /**
+     * Convert a file in the test resource folder to file.
+     *
+     * @param filename to name of the file in test/resources
+     * @return the file
+     * @throws IOException when there is an IO issue
+     */
+    public static File readFile(final String filename) {
+        return new File(ClassLoader.getSystemClassLoader().getResource(filename).getFile());
+    }
+
+    /**
+     * Convert a file in the test resource folder to a string.
+     *
+     * @param filename to name of the file in test/resources
+     * @return the content of the file as a String
+     * @throws IOException when there is an IO issue
+     */
+    public static String getResourceFileContent(final String filename) throws IOException {
+        final File file = readFile(filename);
+        return new String(Files.readAllBytes(file.toPath()));
+    }
+}
diff --git a/cps-ncmp-rest/src/test/resources/dmi-registration.json b/cps-ncmp-rest/src/test/resources/dmi-registration.json
new file mode 100644 (file)
index 0000000..24bc9f9
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "dmiPlugin": "onap-dmi-plugin",
+  "createdCmHandles": [
+    {
+      "cmHandle": "example-name",
+      "cmHandleProperties": {
+          "subSystemId" : "system-001"
+        }
+    }
+  ]
+}
\ No newline at end of file
index 7286fee..c6bcc0a 100644 (file)
         <artifactId>spock-core</artifactId>
         <scope>test</scope>
     </dependency>
+    <dependency>
+        <groupId>org.spockframework</groupId>
+        <artifactId>spock-spring</artifactId>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-test</artifactId>
+        <scope>test</scope>
+        <exclusions>
+            <exclusion>
+                <groupId>org.junit.vintage</groupId>
+                <artifactId>junit-vintage-engine</artifactId>
+            </exclusion>
+        </exclusions>
+    </dependency>
 </dependencies>
 </project>
index 8cf51c9..6038ea4 100644 (file)
@@ -24,6 +24,7 @@ package org.onap.cps.ncmp.api;
 
 import java.util.Collection;
 import org.checkerframework.checker.nullness.qual.NonNull;
+import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
 
@@ -98,4 +99,11 @@ public interface NetworkCmProxyDataService {
      */
     void replaceNodeTree(@NonNull String cmHandle, @NonNull String parentNodeXpath, @NonNull String jsonData);
 
+    /**
+     * Registration of New CM Handles.
+     *
+     * @param dmiPluginRegistration Dmi Plugin Registration
+     */
+    void updateDmiPluginRegistration(DmiPluginRegistration dmiPluginRegistration);
+
 }
index fd85195..f97ea02 100755 (executable)
 
 package org.onap.cps.ncmp.api.impl;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsQueryService;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.api.models.CmHandle;
+import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
+import org.onap.cps.ncmp.api.models.PersistenceCmHandle;
+import org.onap.cps.ncmp.api.models.PersistenceCmHandlesList;
 import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.exceptions.DataValidationException;
 import org.onap.cps.spi.model.DataNode;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 
+@Slf4j
 @Service
 public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService {
 
     private static final String NF_PROXY_DATASPACE_NAME = "NFP-Operational";
 
-    @Autowired
+    private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
+
+    private static final String NCMP_ANCHOR_NAME = "ncmp-dmi-registry";
+
+    private static final String NCMP_DATA_TOP_PATH = "/dmi-registry";
+
     private CpsDataService cpsDataService;
 
-    @Autowired
+    private ObjectMapper objectMapper;
+
     private CpsQueryService cpsQueryService;
 
+    /**
+     * Constructor Injection for Dependencies.
+     * @param cpsDataService Data Service Interface
+     * @param cpsQueryService Query Service Interface
+     * @param objectMapper Object Mapper
+     */
+    public NetworkCmProxyDataServiceImpl(final CpsDataService cpsDataService,
+        final CpsQueryService cpsQueryService, final ObjectMapper objectMapper) {
+        this.cpsDataService = cpsDataService;
+        this.cpsQueryService = cpsQueryService;
+        this.objectMapper = objectMapper;
+    }
+
     private String getDataspaceName() {
         return NF_PROXY_DATASPACE_NAME;
     }
@@ -82,4 +111,28 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     public void replaceNodeTree(final String cmHandle, final String parentNodeXpath, final String jsonData) {
         cpsDataService.replaceNodeTree(getDataspaceName(), cmHandle, parentNodeXpath, jsonData);
     }
+
+    @Override
+    public void updateDmiPluginRegistration(final DmiPluginRegistration dmiPluginRegistration) {
+        try {
+            final List<PersistenceCmHandle> persistenceCmHandles =
+                new ArrayList<>();
+            for (final CmHandle cmHandle: dmiPluginRegistration.getCreatedCmHandles()) {
+                final PersistenceCmHandle persistenceCmHandle = new PersistenceCmHandle();
+                persistenceCmHandle.setDmiServiceName(dmiPluginRegistration.getDmiPlugin());
+                persistenceCmHandle.setId(cmHandle.getCmHandle());
+                persistenceCmHandle.setAdditionalProperties(cmHandle.getCmHandleProperties());
+                persistenceCmHandles.add(persistenceCmHandle);
+            }
+            final PersistenceCmHandlesList persistenceCmHandlesList = new PersistenceCmHandlesList();
+            persistenceCmHandlesList.setCmHandles(persistenceCmHandles);
+            final String cmHandleJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList);
+            cpsDataService.saveListNodeData(NCMP_DATASPACE_NAME, NCMP_ANCHOR_NAME, NCMP_DATA_TOP_PATH,
+                cmHandleJsonData);
+        } catch (final JsonProcessingException e) {
+            throw new DataValidationException(
+                "Parsing error occurred while processing DMI Plugin Registration" + dmiPluginRegistration, e
+                .getMessage(), e);
+        }
+    }
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java
new file mode 100644 (file)
index 0000000..0d1c769
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.models;
+
+import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * CmHandle.
+ */
+@Validated
+@Getter
+@Setter
+public class CmHandle {
+
+    private String cmHandle;
+
+    private Map<String, String> cmHandleProperties;
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java
new file mode 100644 (file)
index 0000000..4017c4a
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+
+package org.onap.cps.ncmp.api.models;
+
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * DmiRegistry.
+ */
+@Getter
+@Setter
+public class DmiPluginRegistration {
+
+    private String dmiPlugin;
+
+    private List<CmHandle> createdCmHandles;
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandle.java
new file mode 100644 (file)
index 0000000..a3f4704
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+
+package org.onap.cps.ncmp.api.models;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * DmiRegistry.
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class PersistenceCmHandle {
+
+    private String id;
+
+    @JsonProperty("dmi-service-name")
+    private String dmiServiceName;
+
+    @JsonProperty("additional-properties")
+    private List<AdditionalProperty> additionalProperties;
+
+    /**
+     * Set Additional Properties map, key and value pair.
+     * @param additionalPropertiesAsMap Map of Additional Properties
+     */
+    public void setAdditionalProperties(final Map<String, String> additionalPropertiesAsMap) {
+        additionalProperties = new ArrayList<>(additionalPropertiesAsMap.size());
+        for (final Map.Entry<String, String> entry : additionalPropertiesAsMap.entrySet()) {
+            additionalProperties.add(new AdditionalProperty(entry.getKey(), entry.getValue()));
+        }
+    }
+
+    @AllArgsConstructor
+    private static class AdditionalProperty {
+
+        @JsonProperty()
+        private final String name;
+
+        @JsonProperty()
+        private final String value;
+    }
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java
new file mode 100644 (file)
index 0000000..95e8515
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.models;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PersistenceCmHandlesList {
+
+    @JsonProperty("cm-handles")
+    private List<PersistenceCmHandle> cmHandles;
+
+    public void setCmHandles(final List<PersistenceCmHandle> cmHandlesAsList) {
+        cmHandles = new ArrayList<>(cmHandlesAsList);
+    }
+
+}
  *  SPDX-License-Identifier: Apache-2.0
  *  ============LICENSE_END=========================================================
  */
+package org.onap.cps.ncmp.api.impl
 
-package org.onap.cps.api.impl
-
+import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsQueryService
-import org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServiceImpl
+import org.onap.cps.ncmp.api.models.CmHandle
+import org.onap.cps.ncmp.api.models.DmiPluginRegistration
 import org.onap.cps.spi.FetchDescendantsOption
 import spock.lang.Specification
 
 class NetworkCmProxyDataServiceImplSpec extends Specification {
-    def objectUnderTest = new NetworkCmProxyDataServiceImpl()
-    def mockcpsDataService = Mock(CpsDataService)
-    def mockcpsQueryService = Mock(CpsQueryService)
 
-    def setup() {
-        objectUnderTest.cpsDataService = mockcpsDataService
-        objectUnderTest.cpsQueryService = mockcpsQueryService
-    }
+    def mockCpsDataService = Mock(CpsDataService)
+    def mockCpsQueryService = Mock(CpsQueryService)
+    def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockCpsDataService, mockCpsQueryService, new ObjectMapper())
 
     def cmHandle = 'some handle'
     def expectedDataspaceName = 'NFP-Operational'
-
     def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
         given: 'a cm Handle and a cps path'
             def cpsPath = '/cps-path'
         when: 'queryDataNodes is invoked'
             objectUnderTest.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption)
         then: 'the persistence service is called once with the correct parameters'
-            1 * mockcpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
+            1 * mockCpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
         where: 'all fetch descendants options are supported'
             fetchDescendantsOption << FetchDescendantsOption.values()
     }
-
     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)
+            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'
@@ -71,9 +65,8 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         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)
+            1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData)
     }
-
     def 'Add list-node elements.'() {
         given: 'a cm handle and parent node xpath'
             def jsonData = 'some json'
@@ -81,9 +74,8 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         when: 'addListNodeElements is invoked'
             objectUnderTest.addListNodeElements(cmHandle, xpath, jsonData)
         then: 'the CPS service method is invoked once with the expected parameters'
-            1 * mockcpsDataService.saveListNodeData(expectedDataspaceName, cmHandle, xpath, jsonData)
+            1 * mockCpsDataService.saveListNodeData(expectedDataspaceName, cmHandle, xpath, jsonData)
     }
-
     def 'Update data node leaves.'() {
         given: 'a cm Handle and a cps path'
             def xpath = '/xpath'
@@ -91,9 +83,8 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         when: 'updateNodeLeaves is invoked'
             objectUnderTest.updateNodeLeaves(cmHandle, xpath, jsonData)
         then: 'the persistence service is called once with the correct parameters'
-            1 * mockcpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData)
+            1 * mockCpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData)
     }
-
     def 'Replace data node tree.'() {
         given: 'a cm Handle and a cps path'
             def xpath = '/xpath'
@@ -101,6 +92,20 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         when: 'replaceNodeTree is invoked'
             objectUnderTest.replaceNodeTree(cmHandle, xpath, jsonData)
         then: 'the persistence service is called once with the correct parameters'
-            1 * mockcpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData)
+            1 * mockCpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData)
+    }
+    def 'Register CM Handle Event.'() {
+        given: 'a registration '
+            def dmiPluginRegistration = new DmiPluginRegistration()
+            dmiPluginRegistration.dmiPlugin = 'my-server'
+            def cmHandle = new CmHandle()
+            cmHandle.cmHandle = '123'
+            cmHandle.cmHandleProperties = [ name1: 'value1', name2: 'value2']
+            dmiPluginRegistration.createdCmHandles = [ cmHandle ]
+            def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
+        when: 'registration is updated'
+            objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
+        then: 'the CPS service method is invoked once with the expected parameters'
+            1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData)
     }
 }
diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml
new file mode 100644 (file)
index 0000000..8ffb882
--- /dev/null
@@ -0,0 +1,22 @@
+#  ============LICENSE_START=======================================================
+#  Copyright (C) 2021 Nordix Foundation
+#  ================================================================================
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+#  SPDX-License-Identifier: Apache-2.0
+#  ============LICENSE_END=========================================================
+
+rest:
+    api:
+        ncmp-base-path: /cps-ncmp/api
+spring:
index d8660d0..40840db 100755 (executable)
                     <excludes>
                         <exclude>org/onap/cps/rest/model/*</exclude>
                         <exclude>org/onap/cps/cpspath/parser/antlr4/*</exclude>
+                        <exclude>org/onap/cps/ncmp/rest/model/*</exclude>
                     </excludes>
                 </configuration>
                 <executions>
index 2d997e3..6c01583 100644 (file)
@@ -33,3 +33,5 @@ databaseChangeLog:
       file: changelog/db/changes/08-update-yang-resources.yaml
   - include:
       file: changelog/db/changes/09-loadData-dmi-registry-schema-set.yaml
+  - include:
+      file: changelog/db/changes/10-loadData-dmi-registry-fragment.yaml
index 7b56e6a..d9be36a 100644 (file)
@@ -111,5 +111,4 @@ databaseChangeLog:
         tableName: 'anchor'
       rollback:
         - sql:
-            sql: delete from anchor where name = 'ncmp-dmi-registry'
-
+            sql: delete from anchor where name = 'ncmp-dmi-registry'
\ No newline at end of file
diff --git a/cps-ri/src/main/resources/changelog/db/changes/10-loadData-dmi-registry-fragment.yaml b/cps-ri/src/main/resources/changelog/db/changes/10-loadData-dmi-registry-fragment.yaml
new file mode 100644 (file)
index 0000000..8325690
--- /dev/null
@@ -0,0 +1,53 @@
+databaseChangeLog:
+  - changeSet:
+      author: cps
+      label: dmi-registry-schema-preload
+      id: 10
+      loadUpdateData:
+        encoding: UTF-8
+        file: 'changelog/db/changes/data/dmi/fragment.csv'
+        onlyUpdate: 'false'
+        primaryKey: 'id'
+        quotchar: '"'
+        separator: '|'
+        tableName: 'fragment'
+        columns:
+          - column:
+              header: id
+              name: id
+              type: NUMERIC
+          - column:
+              header: xpath
+              name: xpath
+              type: STRING
+          - column:
+              header: attributes
+              name: attributes
+              type: CLOB
+          - column:
+              header: anchor_id
+              name: anchor_id
+              type: NUMERIC
+          - column:
+              header: parent_id
+              name: parent_id
+              type: NUMERIC
+          - column:
+              header: dataspace_id
+              name: dataspace_id
+              type: NUMERIC
+          - column:
+              header: schema_node_id
+              name: schema_node_id
+              type: NUMERIC
+
+  - changeSet:
+      author: cps
+      label: dmi-registry-schema-preload
+      id: 10.1
+      changes:
+        - sql:
+            comment: Fixes the id sequence after data insert with predefined ids
+            dbms: postgresql
+            sql: ALTER SEQUENCE IF EXISTS fragment_id_seq RESTART WITH 200
+
diff --git a/cps-ri/src/main/resources/changelog/db/changes/data/dmi/fragment.csv b/cps-ri/src/main/resources/changelog/db/changes/data/dmi/fragment.csv
new file mode 100644 (file)
index 0000000..52aef0e
--- /dev/null
@@ -0,0 +1,2 @@
+id|xpath|attributes|anchor_id|parent_id|dataspace_id|schema_node_id
+144|/dmi-registry|{}|(select id from anchor where name='ncmp-dmi-registry')|null|(select id from dataspace where name='NCMP-Admin')|null
\ No newline at end of file
index 059b560..d8731ac 100644 (file)
       <Bug pattern="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" />
       <Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" />
 
+      <!-- https://stackoverflow.com/a/34674776. Doesn't detect Lombok All Args Constructor variables being used with map get key and value, which can lead to spotbugs being detected
+      on used fields -->
+      <Bug pattern="URF_UNREAD_FIELD"/>
+
       <!-- https://github.com/spotbugs/spotbugs/issues/511. Strict reading of Object.equals() contract means that
            evenever equals() behaviour is defined, all implementations need to adhere to it. The only reason
            to override the method (assuming correct API design, of course) is to provide a more efficient