Registration Response for Update and Delete cmhandles operations 91/127891/15
authorRenu Kumari <renu.kumari@bell.ca>
Fri, 18 Mar 2022 18:32:10 +0000 (14:32 -0400)
committerRenu Kumari <renu.kumari@bell.ca>
Fri, 25 Mar 2022 16:28:15 +0000 (16:28 +0000)
- Process registration in the delete, create and update order
- updateRegistration returns DMIRegistartionRespons that contains
  response for all operations
- Added logic to not delete cmhandle if schemaset deletion fails

Issue-ID: CPS-896
Signed-off-by: Renu Kumari <renu.kumari@bell.ca>
Change-Id: I907ff678d478894e7d4d3cc24b23a979c02ed97a

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/impl/NetworkCmProxyDataServicePropertyHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy

index d942d26..7f4c18f 100644 (file)
@@ -3,6 +3,7 @@
  *  Copyright (C) 2021 highstreet technologies GmbH
  *  Modifications Copyright (C) 2021-2022 Nordix Foundation
  *  Modifications Copyright (C) 2021 Pantheon.tech
+ *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -26,6 +27,7 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum
 
 import java.util.Collection;
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
+import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 import org.onap.cps.spi.model.ModuleReference;
 
@@ -38,8 +40,9 @@ public interface NetworkCmProxyDataService {
      * Registration of New CM Handles.
      *
      * @param dmiPluginRegistration Dmi Plugin Registration
+     * @return dmiPluginRegistrationResponse
      */
-    void updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration);
+    DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration);
 
     /**
      * Get resource data for data store pass-through operational
index 76d4cef..576c45c 100755 (executable)
@@ -3,7 +3,7 @@
  *  Copyright (C) 2021 highstreet technologies GmbH
  *  Modifications Copyright (C) 2021-2022 Nordix Foundation
  *  Modifications Copyright (C) 2021 Pantheon.tech
- *  Modifications Copyright (C) 2021 Bell Canada
+ *  Modifications Copyright (C) 2021-2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum
 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -54,10 +55,14 @@ import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
 import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandlesList;
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError;
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
+import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
 import org.onap.cps.spi.exceptions.DataValidationException;
+import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
 import org.onap.cps.spi.model.ModuleReference;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.http.HttpStatus;
@@ -92,22 +97,28 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     private static final String NO_TOPIC = null;
 
     @Override
-    public void updateDmiRegistrationAndSyncModule(final DmiPluginRegistration dmiPluginRegistration) {
+    public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
+        final DmiPluginRegistration dmiPluginRegistration) {
         dmiPluginRegistration.validateDmiPluginRegistration();
+        final var dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse();
         try {
+            dmiPluginRegistrationResponse.setRemovedCmHandles(
+                parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration.getRemovedCmHandles()));
             if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) {
                 parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration);
             }
             if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) {
-                parseAndUpdateCmHandlesInDmiRegistration(dmiPluginRegistration);
+                dmiPluginRegistrationResponse.setUpdatedCmHandles(
+                    networkCmProxyDataServicePropertyHandler
+                        .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles()));
             }
-            parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration);
         } catch (final JsonProcessingException | DataNodeNotFoundException e) {
             final String errorMessage = String.format(
                     "Error occurred while processing the CM-handle registration request, caused by : [%s]",
                     e.getMessage());
             throw new DataValidationException(errorMessage, e.getMessage(), e);
         }
+        return dmiPluginRegistrationResponse;
     }
 
     @Override
@@ -162,6 +173,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     /**
      * Retrieve cm handle details for a given cm handle.
+     *
      * @param cmHandleId cm handle identifier
      * @return cm handle details
      */
@@ -224,10 +236,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         }
     }
 
-    private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
-        networkCmProxyDataServicePropertyHandler.updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles());
-    }
-
     private YangModelCmHandlesList getUpdatedYangModelCmHandlesList(
         final DmiPluginRegistration dmiPluginRegistration,
         final List<NcmpServiceCmHandle> updatedCmHandles) {
@@ -253,24 +261,37 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         createAnchor(yangModelCmHandle);
     }
 
-    private void parseAndRemoveCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) {
-        for (final String cmHandle : dmiPluginRegistration.getRemovedCmHandles()) {
+    private List<CmHandleRegistrationResponse> parseAndRemoveCmHandlesInDmiRegistration(
+        final List<String> tobeRemovedCmHandles) {
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
+            new ArrayList<>(tobeRemovedCmHandles.size());
+        for (final String cmHandle : tobeRemovedCmHandles) {
             try {
-                attemptToDeleteSchemaSetWithCascade(cmHandle);
+                deleteSchemaSetWithCascade(cmHandle);
                 cpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
                     "/dmi-registry/cm-handles[@id='" + cmHandle + "']", NO_TIMESTAMP);
-            } catch (final DataNodeNotFoundException e) {
-                log.warn("Datanode {} not deleted message {}", cmHandle, e.getMessage());
+                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandle));
+            } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
+                log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
+                    cmHandle, dataNodeNotFoundException.getMessage());
+                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse
+                    .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
+            } catch (final Exception exception) {
+                log.error("Unable to de-register cm-handleIdd : {} , caused by : {}",
+                    cmHandle, exception.getMessage());
+                cmHandleRegistrationResponses.add(
+                    CmHandleRegistrationResponse.createFailureResponse(cmHandle, exception));
             }
         }
+        return cmHandleRegistrationResponses;
     }
 
-    private void attemptToDeleteSchemaSetWithCascade(final String schemaSetName) {
+    private void deleteSchemaSetWithCascade(final String schemaSetName) {
         try {
             cpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
                 CASCADE_DELETE_ALLOWED);
-        } catch (final Exception e) {
-            log.warn("Schema set {} delete failed, reason {}", schemaSetName, e.getMessage());
+        } catch (final SchemaSetNotFoundException schemaSetNotFoundException) {
+            log.warn("Schema set {} does not exist or already deleted", schemaSetName);
         }
     }
 
index 532d846..c838a75 100644 (file)
@@ -67,8 +67,7 @@ public class NetworkCmProxyDataServicePropertyHandler {
      * @param ncmpServiceCmHandles collection of ncmpServiceCmHandles
      */
     public List<CmHandleRegistrationResponse> updateCmHandleProperties(
-        final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles)
-        throws DataNodeNotFoundException {
+        final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
         for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
             final String cmHandle = ncmpServiceCmHandle.getCmHandleID();
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java
new file mode 100644 (file)
index 0000000..ce2f3e6
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Bell Canada
+ *  ================================================================================
+ *  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.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class DmiPluginRegistrationResponse {
+    private List<CmHandleRegistrationResponse> createdCmHandles;
+    private List<CmHandleRegistrationResponse> updatedCmHandles;
+    private List<CmHandleRegistrationResponse> removedCmHandles;
+}
\ No newline at end of file
index e410463..23d2438 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -22,6 +23,7 @@ package org.onap.cps.ncmp.api.impl
 
 import com.fasterxml.jackson.core.JsonProcessingException
 import com.fasterxml.jackson.databind.ObjectMapper
+import java.util.function.Predicate
 import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
@@ -29,14 +31,18 @@ import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
 import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.exceptions.DataValidationException
+import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
 import org.onap.cps.utils.JsonObjectMapper
 import spock.lang.Shared
 import spock.lang.Specification
 
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR
 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
 
 class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
@@ -57,14 +63,31 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     def mockYangModelCmHandleRetriever = Mock(YangModelCmHandleRetriever)
 
     def noTimestamp = null
+    def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
+
+    def 'DMI Registration: Create, Update & Delete operations are processed in the right order'() {
+        given: 'a registration with operations of all three types'
+            def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
+            dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+            dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+            dmiRegistration.setRemovedCmHandles(['cmhandle-2'])
+        when: 'registration is processed'
+            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
+            // Spock validated invocation order between multiple then blocks
+        then: 'cm-handles are removed first'
+            1 * mockCpsDataService.deleteListOrListElement(*_)
+        then: 'cm-handles are created'
+            1 * mockCpsDataService.saveListElements(*_)
+        then: 'cm-handles are updated'
+            1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_)
+    }
 
     def 'Register or re-register a DMI Plugin for the given cm-handle(s) with #scenario process.'() {
         given: 'a registration'
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'my-server')
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
             ncmpServiceCmHandle.cmHandleID = '123'
             ncmpServiceCmHandle.dmiProperties = [dmiProp1: 'dmiValue1', dmiProp2: 'dmiValue2']
-            ncmpServiceCmHandle.publicProperties = [publicProp1: 'publicValue1', publicProp2: 'publicValue2' ]
+            ncmpServiceCmHandle.publicProperties = [publicProp1: 'publicValue1', publicProp2: 'publicValue2']
             dmiPluginRegistration.createdCmHandles = createdCmHandles
             dmiPluginRegistration.updatedCmHandles = updatedCmHandles
             dmiPluginRegistration.removedCmHandles = removedCmHandles
@@ -83,7 +106,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             expectedCallsToDeleteSchemaSetAndListElement * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'cmHandle001', CASCADE_DELETE_ALLOWED)
         and: 'delete list or list element is invoked with the correct parameters'
             expectedCallsToDeleteSchemaSetAndListElement * mockCpsDataService.deleteListOrListElement('NCMP-Admin',
-                    'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
+                'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
         where:
             scenario                    | createdCmHandles      | updatedCmHandles      | removedCmHandles || expectedCallsToSaveNode | expectedCallsToDeleteSchemaSetAndListElement | expectedCallsToUpdateCmHandleProperty
             'create'                    | [ncmpServiceCmHandle] | []                    | []               || 1                       | 0                                            | 0
@@ -93,10 +116,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             'no valid data'             | []                    | []                    | []               || 0                       | 0                                            | 0
     }
 
-    def 'Register a DMI Plugin for the given cm-handle(s) without DMI properties.'() {
+    def 'Create CM-Handle: Register a DMI Plugin for the given cm-handle(s) without DMI properties.'() {
         given: 'a registration without cm-handle properties'
             NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'my-server')
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
             ncmpServiceCmHandle.cmHandleID = '123'
             ncmpServiceCmHandle.dmiProperties = Collections.emptyMap()
             ncmpServiceCmHandle.publicProperties = Collections.emptyMap()
@@ -106,13 +129,12 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'save list elements is invoked with the expected parameters'
             1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
-                    '/dmi-registry', expectedJsonData, noTimestamp)
+                '/dmi-registry', expectedJsonData, noTimestamp)
     }
 
-    def 'Register a DMI Plugin for a given cm-handle(s) with JSON processing errors during process.'() {
+    def 'Create CM-Handle: Register a DMI Plugin for a given cm-handle(s) with JSON processing errors during process.'() {
         given: 'a registration without cm-handle properties '
-            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'some-plugin')
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'some-plugin')
             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
         and: 'an json processing exception occurs'
             spiedJsonObjectMapper.asJsonString(_) >> { throw (new JsonProcessingException('')) }
@@ -122,37 +144,120 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             thrown(DataValidationException)
     }
 
-    def 'Register a DMI Plugin for the given cm-handle(s) with no data found during delete process.'() {
-        given: 'a registration without cm-handle properties '
-            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'some-plugin')
-            dmiPluginRegistration.removedCmHandles = ['some cm handle']
-        and: 'an json processing exception occurs during delete process'
-            mockCpsDataService.deleteListOrListElement(*_) >>  { throw (new DataNodeNotFoundException('','')) }
-        when: 'registration is updated and modules are synced'
-            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+    def 'Update CM-Handle: Update Operation Response is added to the response'() {
+        given: 'a registration to update CmHandles'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                updatedCmHandles: [{}])
+        and: 'cm-handle updates can be processed successfully'
+            def updateOperationResponse = [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-1'),
+                                           CmHandleRegistrationResponse.createFailureResponse('cm-handle-2', new Exception("Failed")),
+                                           CmHandleRegistrationResponse.createFailureResponse('cm-handle-3', CM_HANDLE_DOES_NOT_EXIST)]
+            mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(_) >> updateOperationResponse
+        when: 'registration is updated'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'the response contains updateOperationResponse'
+            assert response.getUpdatedCmHandles().size() == 3
+            assert response.getUpdatedCmHandles().containsAll(updateOperationResponse)
+    }
+
+    def 'Remove CmHandle Successfully: #scenario'() {
+        given: 'a registration'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                removedCmHandles: ['cmhandle'])
+        and: '#scenario'
+            mockCpsModuleService.deleteSchemaSet(_, 'cmhandle', CASCADE_DELETE_ALLOWED) >>
+                { if (!schemaSetExist) { throw new SchemaSetNotFoundException("", "") } }
+        when: 'registration is updated to delete cmhandle'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'delete list or list element is called'
+            1 * mockCpsDataService.deleteListOrListElement(_, _, _, _)
+        and: 'successful response is received'
+            assert response.getRemovedCmHandles().size() == 1
+            with(response.getRemovedCmHandles().get(0)) {
+                assert it.status == CmHandleRegistrationResponse.Status.SUCCESS
+                assert it.cmHandle == 'cmhandle'
+            }
+        where:
+            scenario                                            | schemaSetExist
+            'schema-set exists and can be deleted successfully' | true
+            'schema-set does not exist'                         | false
+    }
+
+    def 'Remove CmHandle: All cm-handles delete requests are processed'() {
+        given: 'a registration with three cm-handles to be deleted'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                removedCmHandles: ['cmhandle1', 'cmhandle2', 'cmhandle3'])
+        and: 'cm-handle deletion is successful for 1st and 3rd; failed for 2nd'
+            mockCpsDataService.deleteListOrListElement(_, _, _, _) >> {} >> { throw new RuntimeException("Failed") } >> {}
+        when: 'registration is updated to delete cmhandles'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'a response is received for all cm-handles'
+            response.getRemovedCmHandles().size() == 3
+        and: '1st and 3rd cm-handle deletes successfully'
+            with(response.getRemovedCmHandles().get(0)) {
+                assert it.status == CmHandleRegistrationResponse.Status.SUCCESS
+            }
+            with(response.getRemovedCmHandles().get(2)) {
+                assert it.status == CmHandleRegistrationResponse.Status.SUCCESS
+            }
+        and: '2nd cmhandle deletion fails'
+            with(response.getRemovedCmHandles().get(1)) {
+                assert it.status == CmHandleRegistrationResponse.Status.FAILURE
+                assert it.registrationError == UNKNOWN_ERROR
+                assert it.errorText == 'Failed'
+            }
+    }
+
+    def 'Remove CmHandle Error Handling: Schema Set Deletion failed'() {
+        given: 'a registration'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                removedCmHandles: ['cmhandle'])
+        and: 'schema set deletion failed with unknown error'
+            mockCpsModuleService.deleteSchemaSet(_, _, _) >> { throw new RuntimeException('Failed') }
+        when: 'registration is updated to delete cmhandle'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'no exception is thrown'
             noExceptionThrown()
+        and: 'cm-handle is not deleted'
+            0 * mockCpsDataService.deleteListOrListElement(_, _, _, _)
+        and: 'a failure response is received'
+            assert response.getRemovedCmHandles().size() == 1
+            with(response.getRemovedCmHandles().get(0)) {
+                assert it.status == CmHandleRegistrationResponse.Status.FAILURE
+                assert it.cmHandle == 'cmhandle'
+                assert it.errorText == 'Failed'
+                assert it.registrationError == UNKNOWN_ERROR
+            }
     }
 
-    def 'Register a DMI Plugin for the given cm-handle(s) with no schema set found during delete process.'() {
+    def 'Remove CmHandle Error Handling: #scenario'() {
         given: 'a registration'
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'my-server')
-            dmiPluginRegistration.removedCmHandles = cmHandlesArray
-        and: 'an exception occurs during delete schema set process'
-            mockCpsModuleService.deleteSchemaSet(_,_,_) >>  { throw (new Exception('')) }
-        when: 'registration is updated and modules are synced'
-            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'delete list or list element is still called'
-            1 * mockCpsDataService.deleteListOrListElement(_,_,_,_)
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                removedCmHandles: ['cmhandle'])
+        and: 'cm-handle deletion throws exception'
+            mockCpsDataService.deleteListOrListElement(_, _, _, _) >> { throw deleteListElementException }
+        when: 'registration is updated to delete cmhandle'
+            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'no exception is thrown'
+            noExceptionThrown()
+        and: 'a failure response is received'
+            assert response.getRemovedCmHandles().size() == 1
+            with(response.getRemovedCmHandles().get(0)) {
+                assert it.status == CmHandleRegistrationResponse.Status.FAILURE
+                assert it.cmHandle == 'cmhandle'
+                assert it.registrationError == expectedError
+                assert it.errorText == expectedErrorText
+            }
+        where:
+            scenario                   | deleteListElementException                | expectedError            | expectedErrorText
+            'cm-handle does not exist' | new DataNodeNotFoundException("", "", "") | CM_HANDLE_DOES_NOT_EXIST | 'cm-handle does not exist'
+            'an unexpected exception'  | new RuntimeException("Failed")            | UNKNOWN_ERROR            | 'Failed'
     }
 
-    def 'Dmi plugin registration with #scenario'() {
+    def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() {
         given: 'a registration '
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:dmiPlugin, dmiModelPlugin:dmiModelPlugin,
-                    dmiDataPlugin:dmiDataPlugin)
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin,
+                dmiDataPlugin: dmiDataPlugin)
             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
         when: 'update registration and sync module is called with correct DMI plugin information'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
@@ -165,11 +270,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             'data & model using same service' | ''         | 'service1'     | 'service1'
     }
 
-    def 'Invalid DMI plugin registration with #scenario'() {
+    def 'Create CM-handle Error Handling: Invalid DMI plugin service name with #scenario'() {
         given: 'a registration '
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:dmiPlugin, dmiModelPlugin:dmiModelPlugin,
-                    dmiDataPlugin:dmiDataPlugin)
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin,
+                dmiDataPlugin: dmiDataPlugin)
             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
         when: 'registration is called with incorrect DMI plugin information'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
@@ -179,36 +283,20 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         and: 'registration is not called'
             0 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)
         where:
-            scenario                        | dmiPlugin  | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails
-            'empty DMI plugins'             | ''         | ''             | ''            || 'No DMI plugin service names'
-            'blank DMI plugins'             | ' '        | ' '            | ' '           || 'No DMI plugin service names'
-            'null DMI plugins'              | null       | null           | null          || 'No DMI plugin service names'
-            'all DMI plugins'               | 'service1' | 'service2'     | 'service3'    || 'Cannot register combined plugin service name and other service names'
-            '(combined)DMI and Data Plugin' | 'service1' | ''             | 'service2'    || 'Cannot register combined plugin service name and other service names'
-            '(combined)DMI and model Plugin'| 'service1' | 'service2'     | ''            || 'Cannot register combined plugin service name and other service names'
-            'only model DMI plugin'         | ''         | 'service1'     | ''            || 'Cannot register just a Data or Model plugin service name'
-            'only data DMI plugin'          | ''         | ''             | 'service1'    || 'Cannot register just a Data or Model plugin service name'
-    }
-
-    def 'Exception thrown on CM-Handle registration update request'() {
-        given: 'a CM-handle registration'
-            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
-        and: 'dmi plugin registration input update request'
-            def dmiPluginReg = new DmiPluginRegistration();
-            dmiPluginReg.dmiPlugin = 'onap.dmap.plugin';
-            dmiPluginReg.updatedCmHandles = [new NcmpServiceCmHandle(cmHandleID: 'unknownHandle')]
-        and: 'update data node leaves is unable to find data node'
-            mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') }
-        when: 'update dmi registration is called'
-            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginReg)
-        then: 'data validation exception is thrown'
-            def exceptionThrown = thrown(DataValidationException.class)
-            assert exceptionThrown.getDetails().contains('DataNode not found')
+            scenario                         | dmiPlugin  | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails
+            'empty DMI plugins'              | ''         | ''             | ''            || 'No DMI plugin service names'
+            'blank DMI plugins'              | ' '        | ' '            | ' '           || 'No DMI plugin service names'
+            'null DMI plugins'               | null       | null           | null          || 'No DMI plugin service names'
+            'all DMI plugins'                | 'service1' | 'service2'     | 'service3'    || 'Cannot register combined plugin service name and other service names'
+            '(combined)DMI and Data Plugin'  | 'service1' | ''             | 'service2'    || 'Cannot register combined plugin service name and other service names'
+            '(combined)DMI and model Plugin' | 'service1' | 'service2'     | ''            || 'Cannot register combined plugin service name and other service names'
+            'only model DMI plugin'          | ''         | 'service1'     | ''            || 'Cannot register just a Data or Model plugin service name'
+            'only data DMI plugin'           | ''         | ''             | 'service1'    || 'Cannot register just a Data or Model plugin service name'
     }
 
     def getObjectUnderTestWithModelSyncDisabled() {
         def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations, mockDmiModelOperations,
-                mockCpsModuleService, mockCpsAdminService, mockNetworkCmProxyDataServicePropertyHandler,mockYangModelCmHandleRetriever))
+            mockCpsModuleService, mockCpsAdminService, mockNetworkCmProxyDataServicePropertyHandler, mockYangModelCmHandleRetriever))
         objectUnderTest.syncModulesAndCreateAnchor(*_) >> null
         return objectUnderTest
     }
index b1ca064..f6264f4 100644 (file)
@@ -135,7 +135,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
                 assert it.errorText == expectedErrorText
             }
         where:
-            scenario                  | exception                                                        || expectedError                              | expectedErrorText
+            scenario                  | exception                                                        || expectedError            | expectedErrorText
             'cmhandle does not exist' | new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') || CM_HANDLE_DOES_NOT_EXIST | 'cm-handle does not exist'
             'unexpected error'        | new RuntimeException('Failed')                                   || UNKNOWN_ERROR            | 'Failed'
     }
index 902ba4e..c5ef2f4 100644 (file)
@@ -42,7 +42,7 @@ class CmHandleRegistrationResponseSpec extends Specification {
     def 'Failed Cm Handle Registration Response: for unexpected exception'() {
         when: 'CMHandle response is created for an unexpected exception'
             def cmHandleRegistrationResponse =
-                CmHandleRegistrationResponse.createFailureResponse('cmHandle', new RuntimeException('unexpected error'))
+                CmHandleRegistrationResponse.createFailureResponse('cmHandle', new Exception('unexpected error'))
         then: 'the response is created with expected value'
             with(cmHandleRegistrationResponse) {
                 assert it.registrationError == RegistrationError.UNKNOWN_ERROR