[TECHDEBT] Align CPS NCMP REST API Specification 47/126847/6
authorJosephKeenan <joseph.keenan@est.tech>
Fri, 28 Jan 2022 11:22:22 +0000 (11:22 +0000)
committerJosephKeenan <joseph.keenan@est.tech>
Wed, 2 Feb 2022 12:06:23 +0000 (12:06 +0000)
- Updated ncmp.yml to align implementation with specification
- Added new Exception classes to differentiate between server NCMP
  issues and client based NCMP issues
- Added 500 error to specification
- To be merged after https://gerrit.onap.org/r/c/cps/+/126848
- Added excpetion handlers for SerNcmpException & DmiRequestException

Issue-ID: CPS-823
Signed-off-by: JosephKeenan <joseph.keenan@est.tech>
Change-Id: If1c9c6c29c6ea2daa07753d7f766ef15c1ba4ca0

12 files changed:
cps-application/src/test/java/org/onap/cps/architecture/LayeredArchitectureTest.java
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmp-inventory.yml
cps-ncmp-rest/docs/openapi/ncmp.yml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
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/exception/DmiRequestException.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/ServerNcmpException.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java
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/NetworkCmProxyDataServiceImplSpec.groovy

index bc5ad18..8bfebc9 100644 (file)
@@ -42,6 +42,7 @@ public class LayeredArchitectureTest {
     private static final String SPI_REPOSITORY_PACKAGE = "org.onap.cps.spi.repository..";
     private static final String YANG_SCHEMA_PACKAGE = "org.onap.cps.yang..";
     private static final String NOTIFICATION_PACKAGE = "org.onap.cps.notification..";
+    private static final String CPS_UTILS_PACKAGE = "org.onap.cps.utils..";
 
     @ArchTest
     static final ArchRule restControllerShouldOnlyDependOnRestController =
@@ -53,7 +54,7 @@ public class LayeredArchitectureTest {
         freeze(classes().that().resideInAPackage(API_SERVICE_PACKAGE)
             .or().resideInAPackage(SPI_SERVICE_PACKAGE).should().onlyHaveDependentClassesThat()
             .resideInAnyPackage(REST_CONTROLLER_PACKAGE, API_SERVICE_PACKAGE, SPI_SERVICE_PACKAGE, NCMP_REST_PACKAGE,
-                NCMP_SERVICE_PACKAGE, YANG_SCHEMA_PACKAGE, NOTIFICATION_PACKAGE));
+                NCMP_SERVICE_PACKAGE, YANG_SCHEMA_PACKAGE, NOTIFICATION_PACKAGE, CPS_UTILS_PACKAGE));
 
     @ArchTest
     static final ArchRule repositoryShouldOnlyBeDependedOnByServicesAndRepository =
index 021a790..22453f3 100644 (file)
@@ -382,3 +382,13 @@ components:
     NoContent:
       description: No Content
       content: {}
+    InternalServerError:
+      description: Internal Server Error
+      content:
+        application/json:
+          schema:
+            $ref: "#/components/schemas/ErrorMessage"
+          example:
+            status: 500
+            message: Internal Server Error
+            details: Internal Server Error occurred
index b0a50aa..f3f84fe 100755 (executable)
@@ -1,5 +1,6 @@
 #  ============LICENSE_START=======================================================
 #  Copyright (C) 2021 Bell Canada
+#  Modifications Copyright (C) 2021-2022 Nordix Foundation
 #  ================================================================================
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -38,3 +39,5 @@ updateDmiRegistration:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
index d80ec65..3a71aba 100755 (executable)
@@ -45,8 +45,8 @@ getResourceDataForPassthroughOperational:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
 resourceDataForPassthroughRunning:
   get:
@@ -76,8 +76,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
   post:
     tags:
       - network-cm-proxy
@@ -112,8 +112,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
   put:
     tags:
@@ -149,8 +149,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
   patch:
     tags:
@@ -180,8 +180,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
   delete:
     tags:
@@ -204,7 +204,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Forbidden'
       404:
         $ref: 'components.yaml#/components/responses/NotFound'
-
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
 fetchModuleReferencesByCmHandle:
   get:
@@ -230,8 +231,8 @@ fetchModuleReferencesByCmHandle:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
 executeCmHandleSearch:
   post:
@@ -259,3 +260,5 @@ executeCmHandleSearch:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
index ce9dd5a..8c6f9f1 100755 (executable)
@@ -21,7 +21,9 @@
 package org.onap.cps.ncmp.rest.exceptions;
 
 import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.impl.exception.DmiRequestException;
 import org.onap.cps.ncmp.api.impl.exception.NcmpException;
+import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException;
 import org.onap.cps.ncmp.rest.controller.NetworkCmProxyController;
 import org.onap.cps.ncmp.rest.model.ErrorMessage;
 import org.onap.cps.spi.exceptions.CpsException;
@@ -56,11 +58,16 @@ public class NetworkCmProxyRestExceptionHandler {
         return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception);
     }
 
-    @ExceptionHandler({NcmpException.class})
-    public static ResponseEntity<Object> handleNcmpExceptions(final NcmpException exception) {
+    @ExceptionHandler({ServerNcmpException.class})
+    public static ResponseEntity<Object> handleServerNcmpExceptions(final ServerNcmpException exception) {
         return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception);
     }
 
+    @ExceptionHandler({DmiRequestException.class})
+    public static ResponseEntity<Object> handleDmiRequestExceptions(final DmiRequestException exception) {
+        return buildErrorResponse(HttpStatus.BAD_REQUEST, exception);
+    }
+
     private static ResponseEntity<Object> buildErrorResponse(final HttpStatus status, final Exception exception) {
         if (exception.getCause() != null || !(exception instanceof CpsException)) {
             log.error("Exception occurred", exception);
index 7b3cd89..1b72b8c 100644 (file)
@@ -23,7 +23,8 @@ package org.onap.cps.ncmp.rest.exceptions
 import groovy.json.JsonSlurper
 import org.modelmapper.ModelMapper
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
-import org.onap.cps.ncmp.api.impl.exception.NcmpException
+import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
+import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
 import org.onap.cps.spi.exceptions.CpsException
 import org.onap.cps.utils.JsonObjectMapper
 import org.spockframework.spring.SpringBean
@@ -34,6 +35,7 @@ import org.springframework.test.web.servlet.MockMvc
 import spock.lang.Shared
 import spock.lang.Specification
 
+import static org.springframework.http.HttpStatus.BAD_REQUEST
 import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
 
@@ -66,17 +68,18 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
         dataNodeBaseEndpoint = "$basePath/v1"
     }
 
-    def 'Get request with generic #scenario exception returns HTTP Status Internal Server Error.'() {
-        when: 'generic CPS exception is thrown by the service'
+    def 'Get request with generic #scenario exception returns correct HTTP Status.'() {
+        when: 'an exception is thrown by the service'
             setupTestException(exception)
             def response = performTestRequest()
-        then: 'an HTTP Internal Server Error response is returned with correct message and details'
-            assertTestResponse(response, INTERNAL_SERVER_ERROR, errorMessage, expectedErrorDetails)
+        then: 'an HTTP response is returned with correct message and details'
+            assertTestResponse(response, expectedErrorCode, errorMessage, expectedErrorDetails)
         where:
-            scenario | exception                                     || expectedErrorDetails
-            'CPS'    | new CpsException(errorMessage, errorDetails)  || errorDetails
-            'NCMP'   | new NcmpException(errorMessage, errorDetails) || null
-            'other'  | new IllegalStateException(errorMessage)       || null
+            scenario      | exception                                           || expectedErrorDetails | expectedErrorCode
+            'CPS'         | new CpsException(errorMessage, errorDetails)        || errorDetails         | INTERNAL_SERVER_ERROR
+            'NCMP-server' | new ServerNcmpException(errorMessage, errorDetails) || null                 | INTERNAL_SERVER_ERROR
+            'NCMP-client' | new DmiRequestException(errorMessage, errorDetails) || null                 | BAD_REQUEST
+            'other'       | new IllegalStateException(errorMessage)             || null                 | INTERNAL_SERVER_ERROR
     }
 
     def setupTestException(exception){
index 82145ef..6a156b8 100755 (executable)
@@ -39,7 +39,7 @@ import org.onap.cps.api.CpsAdminService;
 import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.api.impl.exception.NcmpException;
+import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException;
 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.DmiOperations;
@@ -170,7 +170,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         if (responseEntity.getStatusCode().is2xxSuccessful()) {
             return responseEntity.getBody();
         } else {
-            throw new NcmpException(exceptionMessage,
+            throw new ServerNcmpException(exceptionMessage,
                     "DMI status code: " + responseEntity.getStatusCodeValue()
                             + ", DMI response body: " + responseEntity.getBody());
         }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiRequestException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiRequestException.java
new file mode 100644 (file)
index 0000000..d41ca70
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 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.impl.exception;
+
+/**
+ * Client Based Network CM Proxy exception.
+ */
+public class DmiRequestException extends NcmpException {
+
+    /**
+     * Constructor.
+     *
+     * @param message the error message
+     * @param details the error details
+     */
+    public DmiRequestException(final String message, final String details) {
+        super(message, details);
+    }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/ServerNcmpException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/ServerNcmpException.java
new file mode 100644 (file)
index 0000000..a03a2d3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 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.impl.exception;
+
+/**
+ * Server Based Network CM Proxy exception.
+ */
+public class ServerNcmpException extends NcmpException {
+
+    /**
+     * Constructor.
+     *
+     * @param message the error message
+     * @param details the error details
+     */
+    public ServerNcmpException(final String message, final String details) {
+        super(message, details);
+    }
+}
index 9faf733..f1b3888 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021 Nordix Foundation
+ *  Copyright (C) 2021-2022 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ import com.google.common.base.Strings;
 import java.util.List;
 import lombok.Getter;
 import lombok.Setter;
+import org.onap.cps.ncmp.api.impl.exception.DmiRequestException;
 import org.onap.cps.ncmp.api.impl.exception.NcmpException;
 
 /**
@@ -80,7 +81,7 @@ public class DmiPluginRegistration {
         }
 
         if (errorMessage != null) {
-            throw new NcmpException(errorMessage, "Please supply correct plugin information.");
+            throw new DmiRequestException(errorMessage, "Please supply correct plugin information.");
         }
     }
 
index 3ec6f3a..9f1203d 100644 (file)
@@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
+import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
 import org.onap.cps.ncmp.api.impl.exception.NcmpException
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
@@ -176,8 +177,8 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             dmiPluginRegistration.createdCmHandles = [persistenceCmHandle]
         when: 'registration is called with incorrect DMI plugin information'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'an NcmpException is thrown with correct message details'
-            def exceptionThrown = thrown(NcmpException)
+        then: 'a DMI Request Exception is thrown with correct message details'
+            def exceptionThrown = thrown(DmiRequestException.class)
             assert exceptionThrown.getMessage().contains(expectedMessageDetails)
         and: 'registration is not called'
             0 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)
index 7f12700..9c79d4f 100644 (file)
@@ -35,7 +35,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
-import org.onap.cps.ncmp.api.impl.exception.NcmpException
+import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.model.DataNode
@@ -88,12 +88,11 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'testResourceId', CREATE,
                 '{some-json}', 'application/json')
         then: 'exception is thrown'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
         and: 'details contains (not found) error code: 404'
             exceptionThrown.details.contains('404')
     }
 
-
     def 'Get resource data for pass-through operational from DMI.'() {
         given: 'get data node is called'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
@@ -129,7 +128,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'testAcceptParam',
                 '(a=1,b=2)')
         then: 'exception is thrown with the expected details'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
             exceptionThrown.details == 'DMI status code: 404, DMI response body: NOK-json'
     }
 
@@ -150,7 +149,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'testAcceptParam',
                 '(a=1,b=2)')
         then: 'exception is thrown'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
         and: 'details contains the original response'
             exceptionThrown.details.contains('NOK-json')
     }
@@ -191,7 +190,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'testAcceptParam',
                 '(a=1,b=2)')
         then: 'exception is thrown'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
         and: 'details contains the original response'
             exceptionThrown.details.contains('NOK-json')
     }
@@ -236,7 +235,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 '{some-json}',
                 'application/json')
         then: 'an exception is thrown with the expected error message detailsd with correct operation'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
             exceptionThrown.getMessage().contains(expectedResponseMessage)
         where:
             scenario | givenOperation || expectedResponseMessage