From 440dc8aab179f6c8451683f53b019ccd8bd60bdf Mon Sep 17 00:00:00 2001 From: bmiklos Date: Mon, 5 Sep 2022 18:25:46 +0200 Subject: [PATCH] Handle invalid operations on merged datastore endpoint - Move the other endpoints under the same thats handling the get - Add exceptions to handle invalid cases - Add new tests for testing the invalid cases Issue-ID: CPS-1001 Issue-ID: CPS-1219 Change-Id: I26fd57b5921f976cde7089b4cf1e8c0e5d8dc43b Signed-off-by: bmiklos --- .../controller/NetworkCmProxyStubController.java | 16 +++--- cps-ncmp-rest/docs/openapi/ncmp.yml | 7 ++- cps-ncmp-rest/docs/openapi/openapi.yml | 5 +- .../rest/controller/NetworkCmProxyController.java | 43 +++++++++++++-- .../rest/controller/handlers/DatastoreType.java | 16 +++++- .../rest/exceptions/InvalidDatastoreException.java | 32 +++++++++++ .../NetworkCmProxyRestExceptionHandler.java | 9 ++-- .../controller/NetworkCmProxyControllerSpec.groovy | 63 +++++++++++++++------- 8 files changed, 152 insertions(+), 39 deletions(-) create mode 100644 cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidDatastoreException.java diff --git a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java index ac5337dbd..bb919b5c6 100644 --- a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java +++ b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java @@ -57,13 +57,13 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { private String pathToResponseFiles; @Override - public ResponseEntity getResourceDataForCmHandle(final String dataStoreType, + public ResponseEntity getResourceDataForCmHandle(final String dataStoreName, final String cmHandle, final String resourceIdentifier, final String optionsParamInQuery, final String topicParamInQuery, final Boolean includeDescendants) { - if (DatastoreType.PASSTHROUGH_OPERATIONAL == DatastoreType.fromDatastoreName(dataStoreType)) { + if (DatastoreType.PASSTHROUGH_OPERATIONAL == DatastoreType.fromDatastoreName(dataStoreName)) { final ResponseEntity> asyncResponse = populateAsyncResponse(topicParamInQuery); final Map asyncResponseData = asyncResponse.getBody(); Object responseObject = null; @@ -87,7 +87,8 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity createResourceDataRunningForCmHandle(final String resourceIdentifier, + public ResponseEntity createResourceDataRunningForCmHandle(final String datastoreName, + final String resourceIdentifier, final String cmHandleId, final Object body, final String contentType) { @@ -95,7 +96,8 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity deleteResourceDataRunningForCmHandle(final String cmHandleId, + public ResponseEntity deleteResourceDataRunningForCmHandle(final String datastoreName, + final String cmHandleId, final String resourceIdentifier, final String contentType) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -152,7 +154,8 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity patchResourceDataRunningForCmHandle(final String resourceIdentifier, + public ResponseEntity patchResourceDataRunningForCmHandle(final String datastoreName, + final String resourceIdentifier, final String cmHandleId, final Object body, final String contentType) { @@ -165,7 +168,8 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity updateResourceDataRunningForCmHandle(final String resourceIdentifier, + public ResponseEntity updateResourceDataRunningForCmHandle(final String datastoreName, + final String resourceIdentifier, final String cmHandleId, final Object body, final String contentType) { diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml index 5e22f773a..38db26f47 100755 --- a/cps-ncmp-rest/docs/openapi/ncmp.yml +++ b/cps-ncmp-rest/docs/openapi/ncmp.yml @@ -18,7 +18,7 @@ # SPDX-License-Identifier: Apache-2.0 # ============LICENSE_END========================================================= -getResourceDataForCmHandle: +resourceDataForCmHandle: get: tags: - network-cm-proxy @@ -53,7 +53,6 @@ getResourceDataForCmHandle: 502: $ref: 'components.yaml#/components/responses/BadGateway' -resourceDataForPassthroughRunning: post: tags: - network-cm-proxy @@ -61,6 +60,7 @@ resourceDataForPassthroughRunning: description: create resource data from pass-through running for given cm handle operationId: createResourceDataRunningForCmHandle parameters: + - $ref: 'components.yaml#/components/parameters/datastoreName' - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' @@ -100,6 +100,7 @@ resourceDataForPassthroughRunning: description: Update resource data from pass-through running for the given cm handle operationId: updateResourceDataRunningForCmHandle parameters: + - $ref: 'components.yaml#/components/parameters/datastoreName' - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' @@ -139,6 +140,7 @@ resourceDataForPassthroughRunning: description: Patch resource data from pass-through running for the given cm handle operationId: patchResourceDataRunningForCmHandle parameters: + - $ref: 'components.yaml#/components/parameters/datastoreName' - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' @@ -172,6 +174,7 @@ resourceDataForPassthroughRunning: description: Delete resource data from pass-through running for a given cm handle operationId: deleteResourceDataRunningForCmHandle parameters: + - $ref: 'components.yaml#/components/parameters/datastoreName' - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml index ed15fcd62..4c546beb2 100755 --- a/cps-ncmp-rest/docs/openapi/openapi.yml +++ b/cps-ncmp-rest/docs/openapi/openapi.yml @@ -27,10 +27,7 @@ servers: - url: /ncmp paths: /v1/ch/{cm-handle}/data/ds/{ncmp-datastore-name}: - $ref: 'ncmp.yml#/getResourceDataForCmHandle' - - /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-running: - $ref: 'ncmp.yml#/resourceDataForPassthroughRunning' + $ref: 'ncmp.yml#/resourceDataForCmHandle' /v1/ch/{cm-handle}/modules: $ref: 'ncmp.yml#/fetchModuleReferencesByCmHandle' 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 9aa8263fc..2f6668a35 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 @@ -41,6 +41,7 @@ import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi; import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType; import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandler; import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory; +import org.onap.cps.ncmp.rest.exceptions.InvalidDatastoreException; import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper; import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties; import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters; @@ -98,11 +99,26 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { optionsParamInQuery, topicParamInQuery, includeDescendants); } + /** + * Patch resource data from passthrough-running. + * + * @param resourceIdentifier resource identifier + * @param datastoreName name of the datastore + * @param cmHandle cm handle identifier + * @param requestBody the request body + * @param contentType content type of body + * @return {@code ResponseEntity} response from dmi plugin + */ + @Override public ResponseEntity patchResourceDataRunningForCmHandle(final String resourceIdentifier, + final String datastoreName, final String cmHandle, final Object requestBody, final String contentType) { + + acceptPassthroughRunningOnly(datastoreName); + final Object responseObject = networkCmProxyDataService .writeResourceDataPassThroughRunningForCmHandle( cmHandle, resourceIdentifier, PATCH, @@ -114,6 +130,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * Create resource data in datastore pass-through running for given cm-handle. * * @param resourceIdentifier resource identifier + * @param datastoreName name of the datastore * @param cmHandle cm handle identifier * @param requestBody the request body * @param contentType content type of body @@ -121,9 +138,13 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { */ @Override public ResponseEntity createResourceDataRunningForCmHandle(final String resourceIdentifier, + final String datastoreName, final String cmHandle, final Object requestBody, final String contentType) { + + acceptPassthroughRunningOnly(datastoreName); + networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType); return new ResponseEntity<>(HttpStatus.CREATED); @@ -133,34 +154,43 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * Update resource data in datastore pass-through running for given cm-handle. * * @param resourceIdentifier resource identifier + * @param datastoreName name of the datastore * @param cmHandle cm handle identifier * @param requestBody the request body * @param contentType content type of the body * @return response entity */ + @Override public ResponseEntity updateResourceDataRunningForCmHandle(final String resourceIdentifier, + final String datastoreName, final String cmHandle, final Object requestBody, final String contentType) { + acceptPassthroughRunningOnly(datastoreName); + networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, resourceIdentifier, UPDATE, jsonObjectMapper.asJsonString(requestBody), contentType); return new ResponseEntity<>(HttpStatus.OK); } - /** * Delete resource data in datastore pass-through running for a given cm-handle. * - * @param resourceIdentifier resource identifier + * @param datastoreName name of the datastore * @param cmHandle cm handle identifier + * @param resourceIdentifier resource identifier * @param contentType content type of the body * @return response entity no content if request is successful */ @Override - public ResponseEntity deleteResourceDataRunningForCmHandle(final String cmHandle, + public ResponseEntity deleteResourceDataRunningForCmHandle(final String datastoreName, + final String cmHandle, final String resourceIdentifier, final String contentType) { + + acceptPassthroughRunningOnly(datastoreName); + networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, resourceIdentifier, DELETE, NO_BODY, contentType); return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -290,6 +320,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { return new ResponseEntity<>(HttpStatus.OK); } + private RestOutputCmHandle toRestOutputCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle) { final RestOutputCmHandle restOutputCmHandle = new RestOutputCmHandle(); final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties(); @@ -301,6 +332,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { return restOutputCmHandle; } + private void acceptPassthroughRunningOnly(final String datastoreName) { + final DatastoreType datastoreType = DatastoreType.fromDatastoreName(datastoreName); + if (DatastoreType.PASSTHROUGH_RUNNING != datastoreType) { + throw new InvalidDatastoreException(datastoreName + " is not supported"); + } + } } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java index 959c85d14..e8ab997d6 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; import lombok.Getter; +import org.onap.cps.ncmp.rest.exceptions.InvalidDatastoreException; @Getter public enum DatastoreType { @@ -45,8 +46,21 @@ public enum DatastoreType { type -> datastoreNameToDatastoreType.put(type.getDatastoreName(), type)); } + /** + * From datastore name get datastore type. + * + * @param datastoreName the datastore name + * @return the datastore type + */ public static DatastoreType fromDatastoreName(final String datastoreName) { - return datastoreNameToDatastoreType.get(datastoreName); + + final DatastoreType datastoreType = datastoreNameToDatastoreType.get(datastoreName); + + if (null == datastoreType) { + throw new InvalidDatastoreException(datastoreName + " is an invalid datastore name"); + } + + return datastoreType; } } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidDatastoreException.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidDatastoreException.java new file mode 100644 index 000000000..ff13a93e5 --- /dev/null +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidDatastoreException.java @@ -0,0 +1,32 @@ +/* + * ============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.rest.exceptions; + +public class InvalidDatastoreException extends RuntimeException { + /** + * Instantiates a new Invalid datastore exception. + * + * @param message the message + */ + public InvalidDatastoreException(final String message) { + super(message); + } +} 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 98d7f6fd1..f12a1c5f2 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 @@ -59,7 +59,7 @@ public class NetworkCmProxyRestExceptionHandler { */ @ExceptionHandler public static ResponseEntity handleInternalServerErrorExceptions( - final Exception exception) { + final Exception exception) { return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception); } @@ -74,8 +74,8 @@ public class NetworkCmProxyRestExceptionHandler { return wrapDmiErrorResponse(HttpStatus.BAD_GATEWAY, httpClientRequestException); } - @ExceptionHandler({DmiRequestException.class, DataValidationException.class, HttpMessageNotReadableException.class, - InvalidTopicException.class}) + @ExceptionHandler({DmiRequestException.class, DataValidationException.class, + HttpMessageNotReadableException.class, InvalidTopicException.class, InvalidDatastoreException.class}) public static ResponseEntity handleDmiRequestExceptions(final Exception exception) { return buildErrorResponse(HttpStatus.BAD_REQUEST, exception); } @@ -104,7 +104,8 @@ public class NetworkCmProxyRestExceptionHandler { return new ResponseEntity<>(errorMessage, status); } - private static ResponseEntity wrapDmiErrorResponse(final HttpStatus httpStatus, + private static ResponseEntity wrapDmiErrorResponse( + final HttpStatus httpStatus, final HttpClientRequestException httpClientRequestException) { final var dmiErrorMessage = new DmiErrorMessage(); final var dmiErrorResponse = new DmiErrorMessageDmiresponse(); 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 6e461fa59..b6194bc79 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 @@ -51,6 +51,7 @@ 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 org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder import spock.lang.Shared import spock.lang.Specification @@ -242,7 +243,6 @@ class NetworkCmProxyControllerSpec extends Specification { given: 'resource data url' def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" + "?resourceIdentifier=parent/child" - def requestBody = '{"some-key":"some-value"}' when: 'create resource request is performed' def response = mvc.perform( post(url) @@ -476,38 +476,63 @@ class NetworkCmProxyControllerSpec extends Specification { 'disabled' | false } - def 'Get Resource Data from operational without descendants.'() { - given: 'resource data url' + def 'Get Resource Data from operational with or without descendants'() { + given: 'resource data url with descendants #enabled' def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational" + - "?resourceIdentifier=parent/child&include-descendants=false" + "?resourceIdentifier=parent/child&include-descendants=${enabled}" when: 'get data resource request is performed' def response = mvc.perform( get(getUrl) .contentType(MediaType.APPLICATION_JSON) ).andReturn().response - then: 'the NCMP data service is called with getResourceDataOperational' + then: 'the NCMP data service is called with getResourceDataOperational with #descendantsOption' 1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle', 'parent/child', - FetchDescendantsOption.OMIT_DESCENDANTS) + descendantsOption) and: 'response status is Ok' response.status == HttpStatus.OK.value() + where: 'the following parameters are used' + enabled | descendantsOption + false | FetchDescendantsOption.OMIT_DESCENDANTS + true | FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS } - def 'Get Resource Data from operational including descendants.'() { + def 'Attempt execute #operation rest operation on resource data with #scenario'() { given: 'resource data url' - def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational" + - "?resourceIdentifier=parent/child&include-descendants=true" - when: 'get data resource request is performed' + def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/${datastoreInUrl}?resourceIdentifier=parent/child" + when: 'selected request for data resource is performed on url' def response = mvc.perform( - get(getUrl) - .contentType(MediaType.APPLICATION_JSON) - ).andReturn().response - then: 'the NCMP data service is called with getResourceDataOperational' - 1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle', - 'parent/child', - FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) - and: 'response status is Ok' - response.status == HttpStatus.OK.value() + executeRestOperation(operation, url)) + .andReturn().response + then: 'the response status is as expected' + assert response.status == HttpStatus.BAD_REQUEST.value() + and: 'the response is as expected' + assert response.getContentAsString().contains(datastoreInUrl) + where: 'the following parameters are used' + scenario | operation | datastoreInUrl + 'unsupported datastore' | 'POST' | 'ncmp-datastore:operational' + 'invalid datastore' | 'POST' | 'invalid' + 'unsupported datastore' | 'PUT' | 'ncmp-datastore:operational' + 'invalid datastore' | 'PUT' | 'invalid' + 'unsupported datastore' | 'PATCH' | 'ncmp-datastore:operational' + 'invalid datastore' | 'PATCH' | 'invalid' + 'unsupported datastore' | 'DELETE' | 'ncmp-datastore:operational' + 'invalid datastore' | 'DELETE' | 'invalid' + } + + def executeRestOperation(operation, url) { + if (operation == 'POST') { + return post(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody) + } + if (operation == 'PUT') { + return put(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody) + } + if (operation == 'PATCH') { + return patch(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody) + } + if (operation == 'DELETE') { + return delete(url).contentType(MediaType.APPLICATION_JSON_VALUE) + } } def dataStores() { -- 2.16.6