Update CM-Handle registration response 48/128148/9
authorRenu Kumari <renu.kumari@bell.ca>
Mon, 28 Mar 2022 14:22:41 +0000 (10:22 -0400)
committerRenu Kumari <renu.kumari@bell.ca>
Fri, 1 Apr 2022 11:55:06 +0000 (11:55 +0000)
- update openapi.yml with new response structure
- send only details of failed cm-handle operations
- updated csit to validate 200 status

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

cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmp-inventory.yml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java
csit/tests/cps-model-sync/cps-model-sync.robot

index 69225ae..ddce052 100644 (file)
@@ -70,6 +70,54 @@ components:
           items:
             type: string
           example: [my-cm-handle1, my-cm-handle2, my-cm-handle3]
+    DmiPluginRegistrationErrorResponse:
+      type: object
+      properties:
+        failedCreatedCmHandles:
+          type: array
+          items:
+            $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse'
+          example: [
+            {
+              "cmHandle": "my-cm-handle-01",
+              "errorCode": "01",
+              "errorText": "cm-handle already exists"
+            }
+          ]
+        failedUpdatedCmHandles:
+          type: array
+          items:
+            $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse'
+          example: [
+            {
+              "cmHandle": "my-cm-handle-02",
+              "errorCode": "02",
+              "errorText": "cm-handle does not exist"
+            }
+          ]
+        failedRemovedCmHandles:
+          type: array
+          items:
+            $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse'
+          example: [
+            {
+              "cmHandle": "my-cm-handle-02",
+              "errorCode": "02",
+              "errorText": "cm-handle does not exist"
+            }
+          ]
+    CmHandlerRegistrationErrorResponse:
+      type: object
+      properties:
+        cmHandle:
+          type: string
+          example: my-cm-handle
+        errorCode:
+          type: string
+          example: '01'
+        errorText:
+          type: string
+          example: 'cm-handle already exists'
 
     RestInputCmHandle:
       required:
index 3cd8e8b..5e61d09 100755 (executable)
@@ -31,7 +31,7 @@ updateDmiRegistration:
           schema:
             $ref: 'components.yaml#/components/schemas/RestDmiPluginRegistration'
     responses:
-      204:
+      200:
         $ref: 'components.yaml#/components/responses/NoContent'
       400:
         $ref: 'components.yaml#/components/responses/BadRequest'
@@ -40,4 +40,7 @@ updateDmiRegistration:
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
       500:
-        $ref: 'components.yaml#/components/responses/InternalServerError'
+        content:
+          application/json:
+            schema:
+              $ref: 'components.yaml#/components/schemas/DmiPluginRegistrationErrorResponse'
index c9d26f2..e9a69b3 100755 (executable)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021 Bell Canada
+ *  Copyright (C) 2021-2022 Bell Canada
  *  Modifications Copyright (C) 2022 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
 
 package org.onap.cps.ncmp.rest.controller;
 
+import java.util.List;
+import java.util.stream.Collectors;
 import javax.validation.Valid;
 import lombok.RequiredArgsConstructor;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status;
+import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse;
 import org.onap.cps.ncmp.rest.api.NetworkCmProxyInventoryApi;
+import org.onap.cps.ncmp.rest.model.CmHandlerRegistrationErrorResponse;
+import org.onap.cps.ncmp.rest.model.DmiPluginRegistrationErrorResponse;
 import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -41,14 +48,57 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor
 
     /**
      * Update DMI Plugin Registration (used for first registration also).
+     *
      * @param restDmiPluginRegistration the registration data
      */
     @Override
-    public ResponseEntity<Void> updateDmiPluginRegistration(
+    public ResponseEntity updateDmiPluginRegistration(
         final @Valid RestDmiPluginRegistration restDmiPluginRegistration) {
-        networkCmProxyDataService.updateDmiRegistrationAndSyncModule(
-            ncmpRestInputMapper.toDmiPluginRegistration(restDmiPluginRegistration));
-        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+        final DmiPluginRegistrationResponse dmiPluginRegistrationResponse =
+            networkCmProxyDataService.updateDmiRegistrationAndSyncModule(
+                ncmpRestInputMapper.toDmiPluginRegistration(restDmiPluginRegistration));
+        final DmiPluginRegistrationErrorResponse failedRegistrationErrorResponse =
+            getFailureRegistrationResponse(dmiPluginRegistrationResponse);
+        return allRegistrationsSuccessful(failedRegistrationErrorResponse)
+            ? new ResponseEntity<>(HttpStatus.OK)
+            : new ResponseEntity<>(failedRegistrationErrorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+
+    private boolean allRegistrationsSuccessful(
+        final DmiPluginRegistrationErrorResponse dmiPluginRegistrationErrorResponse) {
+        return dmiPluginRegistrationErrorResponse.getFailedCreatedCmHandles().isEmpty()
+            && dmiPluginRegistrationErrorResponse.getFailedUpdatedCmHandles().isEmpty()
+            && dmiPluginRegistrationErrorResponse.getFailedRemovedCmHandles().isEmpty();
+
+    }
+
+    private DmiPluginRegistrationErrorResponse getFailureRegistrationResponse(
+        final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
+        final var dmiPluginRegistrationErrorResponse = new DmiPluginRegistrationErrorResponse();
+        dmiPluginRegistrationErrorResponse.setFailedCreatedCmHandles(
+            getFailedResponses(dmiPluginRegistrationResponse.getCreatedCmHandles()));
+        dmiPluginRegistrationErrorResponse.setFailedUpdatedCmHandles(
+            getFailedResponses(dmiPluginRegistrationResponse.getUpdatedCmHandles()));
+        dmiPluginRegistrationErrorResponse.setFailedRemovedCmHandles(
+            getFailedResponses(dmiPluginRegistrationResponse.getRemovedCmHandles()));
+
+        return dmiPluginRegistrationErrorResponse;
+    }
+
+    private List<CmHandlerRegistrationErrorResponse> getFailedResponses(
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponseList) {
+        return cmHandleRegistrationResponseList.stream()
+            .filter(cmHandleRegistrationResponse -> cmHandleRegistrationResponse.getStatus() == Status.FAILURE)
+            .map(this::toCmHandleRegistrationErrorResponse)
+            .collect(Collectors.toList());
+    }
+
+    private CmHandlerRegistrationErrorResponse toCmHandleRegistrationErrorResponse(
+        final CmHandleRegistrationResponse registrationResponse) {
+        return new CmHandlerRegistrationErrorResponse()
+            .cmHandle(registrationResponse.getCmHandle())
+            .errorCode(registrationResponse.getRegistrationError().errorCode)
+            .errorText(registrationResponse.getErrorText());
     }
 
 }
index 9b1c2e8..30b6beb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021 Bell Canada
+ *  Copyright (C) 2021-2022 Bell Canada
  *  Modifications Copyright (C) 2021-2022 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,11 @@ package org.onap.cps.ncmp.rest.controller
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.TestUtils
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
+import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
+import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse
+import org.onap.cps.ncmp.rest.model.CmHandlerRegistrationErrorResponse
+import org.onap.cps.ncmp.rest.model.DmiPluginRegistrationErrorResponse
 import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration
 import org.onap.cps.utils.JsonObjectMapper
 import org.spockframework.spring.SpringBean
@@ -36,6 +40,9 @@ import org.springframework.http.HttpStatus
 import org.springframework.http.MediaType
 import org.springframework.test.web.servlet.MockMvc
 import spock.lang.Specification
+
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_ALREADY_EXIST
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
 
 @WebMvcTest(NetworkCmProxyInventoryController)
@@ -58,7 +65,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification {
     @Value('${rest.api.ncmp-inventory-base-path}/v1')
     def ncmpBasePathV1
 
-    def 'Dmi plugin registration #scenario' () {
+    def 'Dmi plugin registration #scenario'() {
         given: 'a dmi plugin registration with #scenario'
             def jsonData = TestUtils.getResourceFileContent(dmiRegistrationJson)
         and: 'the expected rest input as an object'
@@ -72,9 +79,9 @@ class NetworkCmProxyInventoryControllerSpec extends Specification {
                     .content(jsonData)
             ).andReturn().response
         then: 'the converted object is forwarded to the registration service'
-            1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(mockDmiPluginRegistration)
+            1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(mockDmiPluginRegistration) >> new DmiPluginRegistrationResponse()
         and: 'response status is no content'
-            response.status ==  HttpStatus.NO_CONTENT.value()
+            response.status == HttpStatus.OK.value()
         where: 'the following registration json is used'
             scenario                                                                       | dmiRegistrationJson
             'multiple services, added, updated and removed cm handles and many properties' | 'dmi_registration_all_singing_and_dancing.json'
@@ -82,7 +89,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification {
             'without any properties'                                                       | 'dmi_registration_without_properties.json'
     }
 
-    def 'Dmi plugin registration with invalid json' () {
+    def 'Dmi plugin registration with invalid json'() {
         given: 'a dmi plugin registration with #scenario'
             def jsonDataWithUndefinedDataLabel = '{"notAdmiPlugin":""}'
         when: 'post request is performed & registration is called with correct DMI plugin information'
@@ -95,4 +102,74 @@ class NetworkCmProxyInventoryControllerSpec extends Specification {
             response.status == HttpStatus.BAD_REQUEST.value()
     }
 
+    def 'DMI Registration: All cm-handles operations processed successfully.'() {
+        given: 'a dmi plugin registration'
+            def dmiRegistrationRequest = '{}'
+        and: 'service can register cm-handles successfully'
+            def dmiRegistrationResponse = new DmiPluginRegistrationResponse(
+                createdCmHandles: [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-1')],
+                updatedCmHandles: [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-2')],
+                removedCmHandles: [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-3')]
+            )
+            mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(*_) >> dmiRegistrationResponse
+        when: 'registration endpoint is invoked'
+            def response = mvc.perform(
+                post("$ncmpBasePathV1/ch")
+                    .contentType(MediaType.APPLICATION_JSON)
+                    .content(dmiRegistrationRequest)
+            ).andReturn().response
+        then: 'response status is ok'
+            response.status == HttpStatus.OK.value()
+        and: 'the response body is empty'
+            response.getContentAsString() == ''
+
+    }
+
+    def 'DMI Registration Error Handling: #scenario.'() {
+        given: 'a dmi plugin registration'
+            def dmiRegistrationRequest = '{}'
+        and: '#scenario: service failed to register few cm-handle'
+            def dmiRegistrationResponse = new DmiPluginRegistrationResponse(
+                createdCmHandles: [createCmHandleResponse],
+                updatedCmHandles: [updateCmHandleResponse],
+                removedCmHandles: [removeCmHandleResponse]
+            )
+            mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(*_) >> dmiRegistrationResponse
+        when: 'registration endpoint is invoked'
+            def response = mvc.perform(
+                post("$ncmpBasePathV1/ch")
+                    .contentType(MediaType.APPLICATION_JSON)
+                    .content(dmiRegistrationRequest)
+            ).andReturn().response
+        then: 'request status is internal server error'
+            response.status == HttpStatus.INTERNAL_SERVER_ERROR.value()
+        and: 'the response body is in the expected format'
+            def responseBody = jsonObjectMapper.convertJsonString(response.getContentAsString(), DmiPluginRegistrationErrorResponse)
+        and: 'contains only the failure responses'
+            responseBody.getFailedCreatedCmHandles() == expectedFailedCreatedCmHandle
+            responseBody.getFailedUpdatedCmHandles() == expectedFailedUpdateCmHandle
+            responseBody.getFailedRemovedCmHandles() == expectedFailedRemovedCmHandle
+        where:
+            scenario               | createCmHandleResponse         | updateCmHandleResponse         | removeCmHandleResponse         || expectedFailedCreatedCmHandle       | expectedFailedUpdateCmHandle        | expectedFailedRemovedCmHandle
+            'only create failed'   | failedResponse('cm-handle-1')  | successResponse('cm-handle-2') | successResponse('cm-handle-3') || [failedRestResponse('cm-handle-1')] | []                                  | []
+            'only update failed'   | successResponse('cm-handle-1') | failedResponse('cm-handle-2')  | successResponse('cm-handle-3') || []                                  | [failedRestResponse('cm-handle-2')] | []
+            'only delete failed'   | successResponse('cm-handle-1') | successResponse('cm-handle-2') | failedResponse('cm-handle-3')  || []                                  | []                                  | [failedRestResponse('cm-handle-3')]
+            'all three failed'     | failedResponse('cm-handle-1')  | failedResponse('cm-handle-2')  | failedResponse('cm-handle-3')  || [failedRestResponse('cm-handle-1')] | [failedRestResponse('cm-handle-2')] | [failedRestResponse('cm-handle-3')]
+            'create update failed' | failedResponse('cm-handle-1')  | failedResponse('cm-handle-2')  | successResponse('cm-handle-3') || [failedRestResponse('cm-handle-1')] | [failedRestResponse('cm-handle-2')] | []
+            'create delete failed' | failedResponse('cm-handle-1')  | successResponse('cm-handle-2') | failedResponse('cm-handle-3')  || [failedRestResponse('cm-handle-1')] | []                                  | [failedRestResponse('cm-handle-3')]
+            'update delete failed' | successResponse('cm-handle-1') | failedResponse('cm-handle-2')  | failedResponse('cm-handle-3')  || []                                  | [failedRestResponse('cm-handle-2')] | [failedRestResponse('cm-handle-3')]
+    }
+
+    def failedRestResponse(cmHandle) {
+        return new CmHandlerRegistrationErrorResponse('cmHandle': cmHandle, 'errorCode': '00', 'errorText': 'Failed')
+    }
+
+    def failedResponse(cmHandle) {
+        return CmHandleRegistrationResponse.createFailureResponse(cmHandle, new RuntimeException("Failed"))
+    }
+
+    def successResponse(cmHandle) {
+        return CmHandleRegistrationResponse.createSuccessResponse(cmHandle)
+    }
+
 }
index ce2f3e6..8a3d264 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.cps.ncmp.api.models;
 
+import java.util.Collections;
 import java.util.List;
 import lombok.Data;
 import lombok.NoArgsConstructor;
@@ -27,7 +28,7 @@ import lombok.NoArgsConstructor;
 @Data
 @NoArgsConstructor
 public class DmiPluginRegistrationResponse {
-    private List<CmHandleRegistrationResponse> createdCmHandles;
-    private List<CmHandleRegistrationResponse> updatedCmHandles;
-    private List<CmHandleRegistrationResponse> removedCmHandles;
+    private List<CmHandleRegistrationResponse> createdCmHandles = Collections.emptyList();
+    private List<CmHandleRegistrationResponse> updatedCmHandles = Collections.emptyList();
+    private List<CmHandleRegistrationResponse> removedCmHandles = Collections.emptyList();
 }
\ No newline at end of file
index dfad948..7de1f3a 100644 (file)
@@ -42,7 +42,7 @@ Register data node and sync modules.
     ${uri}=              Set Variable       ${ncmpInventoryBasePath}/v1/ch
     ${headers}=          Create Dictionary  Content-Type=application/json   Authorization=${auth}
     ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${jsonDataCreate}
-    Should Be Equal As Strings              ${response.status_code}   204
+    Should Be Equal As Strings              ${response.status_code}   200
 
 Get CM Handle details and confirm it has been registered.
     ${uri}=              Set Variable       ${ncmpBasePath}/v1/ch/PNFDemo
@@ -61,7 +61,7 @@ Update data node and sync modules.
     ${uri}=              Set Variable       ${ncmpInventoryBasePath}/v1/ch
     ${headers}=          Create Dictionary  Content-Type=application/json   Authorization=${auth}
     ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${jsonDataUpdate}
-    Should Be Equal As Strings              ${response.status_code}   204
+    Should Be Equal As Strings              ${response.status_code}   200
 
 Get CM Handle details and confirm it has been updated.
     ${uri}=              Set Variable       ${ncmpBasePath}/v1/ch/PNFDemo