diff options
Diffstat (limited to 'cps-ncmp-rest')
16 files changed, 506 insertions, 76 deletions
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml index 69225aed2d..7ed2efe52a 100644 --- a/cps-ncmp-rest/docs/openapi/components.yaml +++ b/cps-ncmp-rest/docs/openapi/components.yaml @@ -1,6 +1,7 @@ # ============LICENSE_START======================================================= # 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. @@ -30,7 +31,23 @@ components: type: string details: type: string - + # DMI Server Exception Schema + DmiErrorMessage: + title: DMI Error Message + type: object + properties: + message: + type: string + example: "Bad Gateway Error Message NCMP" + dmi-response: + type: object + properties: + http-code: + type: integer + example: 400 + body: + type: string + example: Bad Request # Request Schemas RestDmiPluginRegistration: type: object @@ -70,6 +87,33 @@ 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' + failedUpdatedCmHandles: + type: array + items: + $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse' + failedRemovedCmHandles: + type: array + items: + $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse' + CmHandlerRegistrationErrorResponse: + type: object + properties: + cmHandle: + type: string + example: my-cm-handle + errorCode: + type: string + example: '00' + errorText: + type: string + example: 'Unknown error. <error-details>' RestInputCmHandle: required: @@ -146,6 +190,16 @@ components: type: string example: my-module-revision + CmHandleQueryRestParameters: + type: object + title: Cm Handle query parameters for executing cm handle search + properties: + publicCmHandleProperties: + type: object + additionalProperties: + type: string + example: Book Type + RestOutputCmHandle: type: object title: CM handle Details @@ -303,14 +357,6 @@ components: sample 3: value: resourceIdentifier: parent=shops,child=bookstore - acceptParamInHeader: - name: Accept - in: header - required: false - description: Accept parameter for response, if accept parameter is null, that means client can accept any format. - schema: - type: string - enum: [ application/json, application/yang-data+json ] optionsParamInQuery: name: options in: query @@ -434,3 +480,14 @@ components: status: 500 message: Internal Server Error details: Internal Server Error occurred + BadGateway: + description: Bad Gateway + content: + application/json: + schema: + $ref: "#/components/schemas/DmiErrorMessage" + example: + message: "Bad Gateway Error Message NCMP" + dmi-response: + http-code: 400 + body: "Bad Request" diff --git a/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml b/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml index 3cd8e8baf2..0a408c2413 100755 --- a/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml +++ b/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml @@ -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,60 @@ updateDmiRegistration: 403: $ref: 'components.yaml#/components/responses/Forbidden' 500: - $ref: 'components.yaml#/components/responses/InternalServerError' + description: Partial or Complete failure. The error details are provided in the response body and all supported error codes are documented in the example. + content: + application/json: + schema: + $ref: 'components.yaml#/components/schemas/DmiPluginRegistrationErrorResponse' + example: + failedCreatedCmHandles: [ + { + "cmHandle": "my-cm-handle-01", + "errorCode": "00", + "errorText": "Unknown error. <error-details>" + }, + { + "cmHandle": "my-cm-handle-02", + "errorCode": "01", + "errorText": "cm-handle already exists" + }, + { + "cmHandle": "my-cm-handle-03", + "errorCode": "03", + "errorText": "cm-handle has an invalid character(s) in id" + } + ] + failedUpdatedCmHandles: [ + { + "cmHandle": "my-cm-handle-01", + "errorCode": "00", + "errorText": "Unknown error. <error-details>" + }, + { + "cmHandle": "my-cm-handle-02", + "errorCode": "02", + "errorText": "cm-handle does not exist" + }, + { + "cmHandle": "my-cm-handle-03", + "errorCode": "03", + "errorText": "cm-handle has an invalid character(s) in id" + } + ] + failedRemovedCmHandles: [ + { + "cmHandle": "my-cm-handle-01", + "errorCode": "00", + "errorText": "Unknown error. <error-details>" + }, + { + "cmHandle": "my-cm-handle-02", + "errorCode": "02", + "errorText": "cm-handle does not exists" + }, + { + "cmHandle": "my-cm-handle-03", + "errorCode": "03", + "errorText": "cm-handle has an invalid character(s) in id" + } + ] diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml index a9d08b7951..05e4b84853 100755 --- a/cps-ncmp-rest/docs/openapi/ncmp.yml +++ b/cps-ncmp-rest/docs/openapi/ncmp.yml @@ -1,7 +1,7 @@ # ============LICENSE_START======================================================= # 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. @@ -27,7 +27,6 @@ getResourceDataForPassthroughOperational: parameters: - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - - $ref: 'components.yaml#/components/parameters/acceptParamInHeader' - $ref: 'components.yaml#/components/parameters/optionsParamInQuery' - $ref: 'components.yaml#/components/parameters/topicParamInQuery' responses: @@ -48,6 +47,8 @@ getResourceDataForPassthroughOperational: $ref: 'components.yaml#/components/responses/Forbidden' 500: $ref: 'components.yaml#/components/responses/InternalServerError' + 502: + $ref: 'components.yaml#/components/responses/BadGateway' resourceDataForPassthroughRunning: get: @@ -59,7 +60,6 @@ resourceDataForPassthroughRunning: parameters: - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - - $ref: 'components.yaml#/components/parameters/acceptParamInHeader' - $ref: 'components.yaml#/components/parameters/optionsParamInQuery' - $ref: 'components.yaml#/components/parameters/topicParamInQuery' responses: @@ -80,6 +80,8 @@ resourceDataForPassthroughRunning: $ref: 'components.yaml#/components/responses/Forbidden' 500: $ref: 'components.yaml#/components/responses/InternalServerError' + 502: + $ref: 'components.yaml#/components/responses/BadGateway' post: tags: - network-cm-proxy @@ -116,6 +118,8 @@ resourceDataForPassthroughRunning: $ref: 'components.yaml#/components/responses/Forbidden' 500: $ref: 'components.yaml#/components/responses/InternalServerError' + 502: + $ref: 'components.yaml#/components/responses/BadGateway' put: tags: @@ -153,6 +157,8 @@ resourceDataForPassthroughRunning: $ref: 'components.yaml#/components/responses/Forbidden' 500: $ref: 'components.yaml#/components/responses/InternalServerError' + 502: + $ref: 'components.yaml#/components/responses/BadGateway' patch: tags: @@ -184,6 +190,8 @@ resourceDataForPassthroughRunning: $ref: 'components.yaml#/components/responses/Forbidden' 500: $ref: 'components.yaml#/components/responses/InternalServerError' + 502: + $ref: 'components.yaml#/components/responses/BadGateway' delete: tags: @@ -208,6 +216,8 @@ resourceDataForPassthroughRunning: $ref: 'components.yaml#/components/responses/NotFound' 500: $ref: 'components.yaml#/components/responses/InternalServerError' + 502: + $ref: 'components.yaml#/components/responses/BadGateway' fetchModuleReferencesByCmHandle: get: @@ -281,6 +291,33 @@ retrieveCmHandleDetailsById: application/json: schema: $ref: 'components.yaml#/components/schemas/RestOutputCmHandle' + 404: + $ref: 'components.yaml#/components/responses/NotFound' + 500: + $ref: 'components.yaml#/components/responses/InternalServerError' + +queryCmHandles: + post: + description: Execute cm handle query search + tags: + - network-cm-proxy + summary: Execute cm handle query upon a given set of query parameters + operationId: queryCmHandles + requestBody: + required: true + content: + application/json: + schema: + $ref: 'components.yaml#/components/schemas/CmHandleQueryRestParameters' + responses: + 200: + description: OK + content: + application/json: + schema: + type: array + items: + type: string 400: $ref: 'components.yaml#/components/responses/BadRequest' 401: diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml index 12a8318efb..935b657e1f 100755 --- a/cps-ncmp-rest/docs/openapi/openapi.yml +++ b/cps-ncmp-rest/docs/openapi/openapi.yml @@ -39,4 +39,7 @@ paths: $ref: 'ncmp.yml#/executeCmHandleSearch' /v1/ch/{cm-handle}: - $ref: 'ncmp.yml#/retrieveCmHandleDetailsById'
\ No newline at end of file + $ref: 'ncmp.yml#/retrieveCmHandleDetailsById' + + /v1/data/ch/searches: + $ref: 'ncmp.yml#/queryCmHandles' diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml index 97305cfe98..6a700c3e12 100644 --- a/cps-ncmp-rest/pom.xml +++ b/cps-ncmp-rest/pom.xml @@ -27,7 +27,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>3.0.0-SNAPSHOT</version> + <version>3.1.0-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java index 4c8fafea5f..a9ec863d53 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java @@ -45,7 +45,7 @@ public interface NcmpRestInputMapper { nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT) DmiPluginRegistration toDmiPluginRegistration(final RestDmiPluginRegistration restDmiPluginRegistration); - @Mapping(source = "cmHandle", target = "cmHandleID") + @Mapping(source = "cmHandle", target = "cmHandleId") @Mapping(source = "cmHandleProperties", target = "dmiProperties") @Mapping(source = "publicCmHandleProperties", target = "publicProperties") NcmpServiceCmHandle toNcmpServiceCmHandle(final RestInputCmHandle restInputCmHandle); diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java index 0201fad2b5..5c1f8704da 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java @@ -3,7 +3,7 @@ * Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Nordix Foundation * Modification Copyright (C) 2021 highstreet technologies GmbH - * Modifications (C) 2021 Bell Canada + * Modifications (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. @@ -31,18 +31,25 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; import javax.validation.Valid; import javax.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; +import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException; +import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi; import org.onap.cps.ncmp.rest.model.CmHandleProperties; import org.onap.cps.ncmp.rest.model.CmHandleProperty; import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties; +import org.onap.cps.ncmp.rest.model.CmHandleQueryRestParameters; import org.onap.cps.ncmp.rest.model.CmHandles; import org.onap.cps.ncmp.rest.model.ConditionProperties; import org.onap.cps.ncmp.rest.model.Conditions; @@ -50,6 +57,7 @@ import org.onap.cps.ncmp.rest.model.ModuleNameAsJsonObject; import org.onap.cps.ncmp.rest.model.ModuleNamesAsJsonArray; import org.onap.cps.ncmp.rest.model.RestModuleReference; import org.onap.cps.ncmp.rest.model.RestOutputCmHandle; +import org.onap.cps.utils.CpsValidator; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -63,6 +71,9 @@ import org.springframework.web.bind.annotation.RestController; public class NetworkCmProxyController implements NetworkCmProxyApi { private static final String NO_BODY = null; + private static final String NO_REQUEST_ID = null; + private static final String NO_TOPIC = null; + public static final String ASYNC_REQUEST_ID = "requestId"; private final NetworkCmProxyDataService networkCmProxyDataService; private final JsonObjectMapper jsonObjectMapper; @@ -73,7 +84,6 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * * @param cmHandle cm handle identifier * @param resourceIdentifier resource identifier - * @param acceptParamInHeader accept header parameter * @param optionsParamInQuery options query parameter * @param topicParamInQuery topic query parameter * @return {@code ResponseEntity} response from dmi plugin @@ -81,15 +91,21 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { @Override public ResponseEntity<Object> getResourceDataOperationalForCmHandle(final String cmHandle, final @NotNull @Valid String resourceIdentifier, - final String acceptParamInHeader, final @Valid String optionsParamInQuery, final @Valid String topicParamInQuery) { + final ResponseEntity<Map<String, Object>> asyncResponse = populateAsyncResponse(topicParamInQuery); + final Map<String, Object> asyncResponseData = asyncResponse.getBody(); + final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(cmHandle, resourceIdentifier, - acceptParamInHeader, optionsParamInQuery, - topicParamInQuery); - return ResponseEntity.ok(responseObject); + asyncResponseData == null ? NO_TOPIC : topicParamInQuery, + asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString()); + + if (asyncResponseData == null) { + return ResponseEntity.ok(responseObject); + } + return ResponseEntity.ok(asyncResponse); } /** @@ -97,7 +113,6 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * * @param cmHandle cm handle identifier * @param resourceIdentifier resource identifier - * @param acceptParamInHeader accept header parameter * @param optionsParamInQuery options query parameter * @param topicParamInQuery topic query parameter * @return {@code ResponseEntity} response from dmi plugin @@ -105,15 +120,21 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { @Override public ResponseEntity<Object> getResourceDataRunningForCmHandle(final String cmHandle, final @NotNull @Valid String resourceIdentifier, - final String acceptParamInHeader, final @Valid String optionsParamInQuery, final @Valid String topicParamInQuery) { + final ResponseEntity<Map<String, Object>> asyncResponse = populateAsyncResponse(topicParamInQuery); + final Map<String, Object> asyncResponseData = asyncResponse.getBody(); + final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(cmHandle, resourceIdentifier, - acceptParamInHeader, optionsParamInQuery, - topicParamInQuery); - return ResponseEntity.ok(responseObject); + asyncResponseData == null ? NO_TOPIC : topicParamInQuery, + asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString()); + + if (asyncResponseData == null) { + return ResponseEntity.ok(responseObject); + } + return ResponseEntity.ok(asyncResponse); } @Override @@ -195,6 +216,19 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { } /** + * Query and return cm handles that match the given query parameters. + * + * @param cmHandleQueryRestParameters the cm handle query parameters + * @return collection of cm handle ids + */ + public ResponseEntity<List<String>> queryCmHandles( + final CmHandleQueryRestParameters cmHandleQueryRestParameters) { + final Set<String> cmHandleIds = networkCmProxyDataService.queryCmHandles( + jsonObjectMapper.convertToValueType(cmHandleQueryRestParameters, CmHandleQueryApiParameters.class)); + return ResponseEntity.ok(List.copyOf(cmHandleIds)); + } + + /** * Search for Cm Handle and Properties by Name. * @param cmHandleId cm-handle identifier * @return cm handle and its properties @@ -258,9 +292,38 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { private RestOutputCmHandle toRestOutputCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle) { final RestOutputCmHandle restOutputCmHandle = new RestOutputCmHandle(); final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties(); - restOutputCmHandle.setCmHandle(ncmpServiceCmHandle.getCmHandleID()); + restOutputCmHandle.setCmHandle(ncmpServiceCmHandle.getCmHandleId()); cmHandlePublicProperties.add(ncmpServiceCmHandle.getPublicProperties()); restOutputCmHandle.setPublicCmHandleProperties(cmHandlePublicProperties); return restOutputCmHandle; } + + private ResponseEntity<Map<String, Object>> populateAsyncResponse(final String topicParamInQuery) { + final boolean processAsynchronously = hasTopicParameter(topicParamInQuery); + final Map<String, Object> responseData; + if (processAsynchronously) { + responseData = getAsyncResponseData(); + } else { + responseData = null; + } + return ResponseEntity.ok().body(responseData); + } + + private static boolean hasTopicParameter(final String topicName) { + if (topicName == null) { + return false; + } + if (CpsValidator.validateTopicName(topicName)) { + return true; + } + throw new InvalidTopicException("Topic name " + topicName + " is invalid", "invalid topic"); + } + + private Map<String, Object> getAsyncResponseData() { + final Map<String, Object> asyncResponseData = new HashMap<>(1); + final String resourceDataRequestId = UUID.randomUUID().toString(); + asyncResponseData.put(ASYNC_REQUEST_ID, resourceDataRequestId); + return asyncResponseData; + } + } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java index c9d26f2a54..105a6a559c 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java @@ -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"); @@ -21,10 +21,17 @@ 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,58 @@ 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 DmiPluginRegistrationErrorResponse 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()); } } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java index 0843e9741e..c72373344d 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java @@ -24,11 +24,14 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.exception.DmiRequestException; +import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException; import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException; 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.controller.NetworkCmProxyInventoryController; +import org.onap.cps.ncmp.rest.model.DmiErrorMessage; +import org.onap.cps.ncmp.rest.model.DmiErrorMessageDmiresponse; import org.onap.cps.ncmp.rest.model.ErrorMessage; import org.onap.cps.spi.exceptions.CpsException; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; @@ -66,6 +69,12 @@ public class NetworkCmProxyRestExceptionHandler { return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception); } + @ExceptionHandler({HttpClientRequestException.class}) + public static ResponseEntity<Object> handleClientRequestExceptions( + final HttpClientRequestException httpClientRequestException) { + return wrapDmiErrorResponse(HttpStatus.BAD_GATEWAY, httpClientRequestException); + } + @ExceptionHandler({DmiRequestException.class, DataValidationException.class, HttpMessageNotReadableException.class, InvalidTopicException.class}) public static ResponseEntity<Object> handleDmiRequestExceptions(final Exception exception) { @@ -91,8 +100,19 @@ public class NetworkCmProxyRestExceptionHandler { } else { errorMessage.setDetails(CHECK_LOGS_FOR_DETAILS); } - errorMessage.setDetails(exception instanceof CpsException ? ((CpsException) exception).getDetails() : - CHECK_LOGS_FOR_DETAILS); + errorMessage.setDetails( + exception instanceof CpsException ? ((CpsException) exception).getDetails() : CHECK_LOGS_FOR_DETAILS); return new ResponseEntity<>(errorMessage, status); } + + private static ResponseEntity<Object> wrapDmiErrorResponse(final HttpStatus httpStatus, + final HttpClientRequestException httpClientRequestException) { + final var dmiErrorMessage = new DmiErrorMessage(); + final var dmiErrorResponse = new DmiErrorMessageDmiresponse(); + dmiErrorResponse.setHttpCode(httpClientRequestException.getHttpStatus()); + dmiErrorResponse.setBody(httpClientRequestException.getDetails()); + dmiErrorMessage.setMessage(httpClientRequestException.getMessage()); + dmiErrorMessage.setDmiResponse(dmiErrorResponse); + return new ResponseEntity<>(dmiErrorMessage, httpStatus); + } } diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapperSpec.groovy index 3d54a0b089..bb762080d2 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapperSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapperSpec.groovy @@ -43,7 +43,7 @@ class NcmpRestInputMapperSpec extends Specification { then: 'the result returns the correct number of cm handles' result.createdCmHandles.size() == 1 and: 'the converted cm handle has the same id' - result.createdCmHandles[0].cmHandleID == 'example-id' + result.createdCmHandles[0].cmHandleId == 'example-id' and: '(empty) properties are converted correctly' result.createdCmHandles[0].dmiProperties == expectedDmiProperties result.createdCmHandles[0].publicProperties == expectedPublicProperties diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy index d5c3cd9f37..b34b0fff38 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy @@ -3,7 +3,7 @@ * Copyright (C) 2021 Pantheon.tech * Modification Copyright (C) 2021 highstreet technologies GmbH * Modification Copyright (C) 2021-2022 Nordix Foundation - * Modification Copyright (C) 2021 Bell Canada. + * Modification 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. @@ -60,7 +60,10 @@ class NetworkCmProxyControllerSpec extends Specification { NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock() @SpringBean - JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + ObjectMapper objectMapper = new ObjectMapper() + + @SpringBean + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(objectMapper) @SpringBean NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper) @@ -72,6 +75,7 @@ class NetworkCmProxyControllerSpec extends Specification { @Shared def NO_TOPIC = null + def NO_REQUEST_ID = null def 'Get Resource Data from pass-through operational.'() { given: 'resource data url' @@ -81,43 +85,51 @@ class NetworkCmProxyControllerSpec extends Specification { def response = mvc.perform( get(getUrl) .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON_VALUE) ).andReturn().response then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle' 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle', 'parent/child', - 'application/json', '(a=1,b=2)', - NO_TOPIC) + NO_TOPIC, + NO_REQUEST_ID) and: 'response status is Ok' response.status == HttpStatus.OK.value() } - def 'Get Resource Data from pass-through operational with #scenario.'() { + def 'Get Resource Data from #datastoreInUrl with #scenario.'() { given: 'resource data url' - def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" + + def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" + "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}" when: 'get data resource request is performed' def response = mvc.perform( get(getUrl) .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON_VALUE) ).andReturn().response then: 'the NCMP data service is called with operational data for cm handle' - 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle', + expectedNumberOfMethodExecutions + * mockNetworkCmProxyDataService."${expectedMethodName}"('testCmHandle', 'parent/child', - 'application/json', '(a=1,b=2)', - expectedTopicName) - and: 'response status is Ok' - response.status == HttpStatus.OK.value() + expectedTopicName, + _) + then: 'response status is expected' + response.status == expectedHttpStatus where: 'the following parameters are used' - scenario | topicQueryParam || expectedTopicName - 'Url with valid topic' | "&topic=my-topic-name" || "my-topic-name" - 'No topic in url' | '' || NO_TOPIC - 'Null topic in url' | "&topic=null" || "null" - 'Empty topic in url' | "&topic=\"\"" || "\"\"" - 'Missing topic in url' | "&topic=" || "" + scenario | datastoreInUrl | topicQueryParam || expectedTopicName | expectedMethodName | expectedNumberOfMethodExecutions | expectedHttpStatus + 'url with valid topic' | 'passthrough-operational' | '&topic=my-topic-name' || 'my-topic-name' | 'getResourceDataOperationalForCmHandle' | 1 | HttpStatus.OK.value() + 'no topic in url' | 'passthrough-operational' | '' || NO_TOPIC | 'getResourceDataOperationalForCmHandle' | 1 | HttpStatus.OK.value() + 'null topic in url' | 'passthrough-operational' | '&topic=null' || 'null' | 'getResourceDataOperationalForCmHandle' | 1 | HttpStatus.OK.value() + 'empty topic in url' | 'passthrough-operational' | '&topic=\"\"' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'missing topic in url' | 'passthrough-operational' | '&topic=' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'blank topic value in url' | 'passthrough-operational' | '&topic=\" \"' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'invalid non-empty topic value in url' | 'passthrough-operational' | '&topic=1_5_*_#' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'url with valid topic' | 'passthrough-running' | '&topic=my-topic-name' || 'my-topic-name' | 'getResourceDataPassThroughRunningForCmHandle' | 1 | HttpStatus.OK.value() + 'no topic in url' | 'passthrough-running' | '' || NO_TOPIC | 'getResourceDataPassThroughRunningForCmHandle' | 1 | HttpStatus.OK.value() + 'null topic in url' | 'passthrough-running' | '&topic=null' || 'null' | 'getResourceDataPassThroughRunningForCmHandle' | 1 | HttpStatus.OK.value() + 'empty topic in url' | 'passthrough-running' | '&topic=\"\"' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'missing topic in url' | 'passthrough-running' | '&topic=' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'blank topic value in url' | 'passthrough-running' | '&topic=\" \"' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'invalid non-empty topic value in url' | 'passthrough-running' | '&topic=1_5_*_#' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() } def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() { @@ -127,14 +139,13 @@ class NetworkCmProxyControllerSpec extends Specification { and: 'ncmp service returns json object' mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle', resourceIdentifier, - 'application/json', '(a=1,b=2)', - NO_TOPIC) >> '{valid-json}' + NO_TOPIC, + NO_REQUEST_ID) >> '{valid-json}' when: 'get data resource request is performed' def response = mvc.perform( get(getUrl) .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON_VALUE) ).andReturn().response then: 'response status is Ok' response.status == HttpStatus.OK.value() @@ -157,8 +168,7 @@ class NetworkCmProxyControllerSpec extends Specification { when: 'update data resource request is performed' def response = mvc.perform( put(updateUrl) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .accept(MediaType.APPLICATION_JSON_VALUE).content(requestBody) + .contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody) ).andReturn().response then: 'ncmp service method to update resource is called' 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', @@ -175,8 +185,7 @@ class NetworkCmProxyControllerSpec extends Specification { when: 'create resource request is performed' def response = mvc.perform( post(url) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .accept(MediaType.APPLICATION_JSON_VALUE).content(requestBody) + .contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody) ).andReturn().response then: 'ncmp service method to create resource called' 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', @@ -222,7 +231,7 @@ class NetworkCmProxyControllerSpec extends Specification { def cmHandleId = 'Some-Cm-Handle' def dmiProperties = [ prop:'some DMI property' ] def publicProperties = [ "public prop":'some public property' ] - def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleID: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties) + def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties) and: 'the service method is invoked with the cm handle id' 1 * mockNetworkCmProxyDataService.getNcmpServiceCmHandle('Some-Cm-Handle') >> ncmpServiceCmHandle when: 'the cm handle details api is invoked' @@ -249,6 +258,31 @@ class NetworkCmProxyControllerSpec extends Specification { response.contentAsString == '{"cmHandles":[]}' } + def 'Query for cm handles matching query parameters'() { + given: 'an endpoint and json data' + def searchesEndpoint = "$ncmpBasePathV1/data/ch/searches" + String jsonString = '{"publicCmHandleProperties": {"name": "Contact", "value": "newemailforstore@bookstore.com"}}' + and: 'the service method is invoked with module names and returns cm handle ids' + 1 * mockNetworkCmProxyDataService.queryCmHandles(_) >> ['some-cmhandle-id1', 'some-cmhandle-id2'] + when: 'the searches api is invoked' + def response = mvc.perform(post(searchesEndpoint) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonString)).andReturn().response + then: 'cm handle ids are returned' + response.contentAsString == '["some-cmhandle-id1","some-cmhandle-id2"]' + } + + def 'Query for cm handles with invalid request payload'() { + when: 'the searches api is invoked' + def searchesEndpoint = "$ncmpBasePathV1/data/ch/searches" + def invalidInputData = '{invalidJson}' + def response = mvc.perform(post(searchesEndpoint) + .contentType(MediaType.APPLICATION_JSON) + .content(invalidInputData)).andReturn().response + then: 'BAD_REQUEST is returned' + response.getStatus() == 400 + } + def 'Patch resource data in pass-through running datastore.' () { given: 'patch resource data url' def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" + @@ -279,5 +313,24 @@ class NetworkCmProxyControllerSpec extends Specification { and: 'the response is No Content' response.status == HttpStatus.NO_CONTENT.value() } + + def 'Get resource data from DMI with valid topic i.e. async request for #scenario'() { + given: 'resource data url' + def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" + + "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=my-topic-name" + when: 'get data resource request is performed' + def response = mvc.perform( + get(getUrl) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON_VALUE) + ).andReturn().response + then: 'async request id is generated' + assert response.contentAsString.contains("requestId") + where: 'the following parameters are used' + scenario | datastoreInUrl + ':passthrough-operational' | 'passthrough-operational' + ':passthrough-running' | 'passthrough-running' + } + } diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy index 9b1c2e87c0..30b6beb379 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy @@ -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) + } + } diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy index b642370154..1f6c38428b 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy @@ -21,14 +21,13 @@ package org.onap.cps.ncmp.rest.exceptions -import com.fasterxml.jackson.databind.ObjectMapper import groovy.json.JsonSlurper import org.mapstruct.factory.Mappers import org.onap.cps.TestUtils import org.onap.cps.ncmp.api.NetworkCmProxyDataService import org.onap.cps.ncmp.api.impl.exception.DmiRequestException +import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.ncmp.rest.controller.NcmpRestInputMapper import org.onap.cps.spi.exceptions.CpsException import org.onap.cps.spi.exceptions.DataNodeNotFoundException @@ -38,6 +37,7 @@ import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.test.web.servlet.MockMvc import spock.lang.Shared @@ -111,6 +111,19 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { assertTestResponse(response, BAD_REQUEST, sampleErrorMessage, sampleErrorDetails) } + def 'Failing DMI Request - passthrough scenario'() { + given: 'failing DMI request' + mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(*_) >> { throw new HttpClientRequestException('Error Message Details NCMP', 'Bad Request from DMI', 400) } + when: 'the DMI request is executed' + def response = mvc.perform(get("$dataNodeBaseEndpointNcmp/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=stores:bookstore/categories=100")) + .andReturn().response + then: 'NCMP service responds with 502 Bad Gateway status' + response.status == HttpStatus.BAD_GATEWAY.value() + and: 'the NCMP response also contains the original DMI response details' + response.contentAsString.contains('400') + response.contentAsString.contains('Bad Request from DMI') + } + def setupTestException(exception, apiType) { if (NCMP == apiType) { mockNetworkCmProxyDataService.getYangResourcesModuleReferences(*_) >> { throw exception } diff --git a/cps-ncmp-rest/src/test/resources/dmi_registration_all_singing_and_dancing.json b/cps-ncmp-rest/src/test/resources/dmi_registration_all_singing_and_dancing.json index fd8b56b02d..c2a307db26 100644 --- a/cps-ncmp-rest/src/test/resources/dmi_registration_all_singing_and_dancing.json +++ b/cps-ncmp-rest/src/test/resources/dmi_registration_all_singing_and_dancing.json @@ -3,7 +3,7 @@ "dmiModelPlugin":"service3", "createdCmHandles":[ { - "cmHandle":"ch1(new)", + "cmHandle":"ch1-new", "cmHandleProperties":{ "dmiProp1":"ch1-dmi1", "dmiProp2":"ch1-dmi2" @@ -14,7 +14,7 @@ } }, { - "cmHandle":"ch2(new)", + "cmHandle":"ch2-new", "cmHandleProperties":{ "dmiProp1":"ch2-dmi1", "dmiProp2":"ch2-dmi2" @@ -27,7 +27,7 @@ ], "updatedCmHandles":[ { - "cmHandle":"ch3(upd)", + "cmHandle":"ch3-upd", "cmHandleProperties":{ "dmiProp1":"ch3-dmi1" }, diff --git a/cps-ncmp-rest/src/test/resources/dmi_registration_updates_only.json b/cps-ncmp-rest/src/test/resources/dmi_registration_updates_only.json index 58a1a9836b..26acdbdcbe 100644 --- a/cps-ncmp-rest/src/test/resources/dmi_registration_updates_only.json +++ b/cps-ncmp-rest/src/test/resources/dmi_registration_updates_only.json @@ -2,7 +2,7 @@ "dmiPlugin": "service1", "updatedCmHandles":[ { - "cmHandle":"ch3(upd)", + "cmHandle":"ch3-upd", "cmHandleProperties":{ "dmiProp1":"ch3-dmi1", "dmiProp2":null diff --git a/cps-ncmp-rest/src/test/resources/dmi_registration_without_properties.json b/cps-ncmp-rest/src/test/resources/dmi_registration_without_properties.json index 395c098d21..a5dd7b0aad 100644 --- a/cps-ncmp-rest/src/test/resources/dmi_registration_without_properties.json +++ b/cps-ncmp-rest/src/test/resources/dmi_registration_without_properties.json @@ -4,7 +4,7 @@ "dmiModelPlugin":"service3", "createdCmHandles":[ { - "cmHandle": "ch1(new)" + "cmHandle": "ch1-new" } ] } |