From e9ed581de0a6090c513e6fca0052b69396cb3cc8 Mon Sep 17 00:00:00 2001 From: kissand Date: Thu, 12 May 2022 15:59:18 +0200 Subject: Merge 2 'query' end points in NCMP - merge two endpoint for a same backend - use xPath query instead of sql query - modify searches endpoint to return a cmHandle object with all public properties - handle old (deprecated) queries - handle public property queries - create useful examples - use more verbose error messages - simplify openapi yamls - create new query service - change second endpoint name to a better matched name - modify legacy tests with new requirements - create new tests for the new scenarios Issue-ID: CPS-1016 Change-Id: I7476e9dbd510ec93b5b48ce85d477ecb2dadffff Signed-off-by: kissand --- .../rest/controller/NetworkCmProxyController.java | 85 ++++++---------------- .../onap/cps/ncmp/rest/util/DeprecationHelper.java | 73 +++++++++++++++++++ .../controller/NetworkCmProxyControllerSpec.groovy | 35 ++++++--- .../NetworkCmProxyRestExceptionHandlerSpec.groovy | 5 +- .../ncmp/rest/util/DeprecationHelperSpec.groovy | 54 ++++++++++++++ 5 files changed, 181 insertions(+), 71 deletions(-) create mode 100644 cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DeprecationHelper.java create mode 100644 cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/DeprecationHelperSpec.groovy (limited to 'cps-ncmp-rest/src') 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 11517bcc9e..ccb1e9bbb8 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 @@ -28,9 +28,6 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH; import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,18 +45,12 @@ import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi; import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor; import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper; -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; -import org.onap.cps.ncmp.rest.model.ModuleNameAsJsonObject; -import org.onap.cps.ncmp.rest.model.ModuleNamesAsJsonArray; +import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters; import org.onap.cps.ncmp.rest.model.RestModuleReference; import org.onap.cps.ncmp.rest.model.RestOutputCmHandle; import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties; +import org.onap.cps.ncmp.rest.util.DeprecationHelper; import org.onap.cps.utils.CpsValidator; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.beans.factory.annotation.Value; @@ -79,6 +70,8 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { private static final String NO_TOPIC = null; private final NetworkCmProxyDataService networkCmProxyDataService; private final JsonObjectMapper jsonObjectMapper; + + private final DeprecationHelper deprecationHelper; private final NcmpRestInputMapper ncmpRestInputMapper; private final RestOutputCmHandleStateMapper restOutputCmHandleStateMapper; private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor; @@ -212,30 +205,35 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { } /** - * Execute cm handle search. + * Query and return cm handles that match the given query parameters. * - * @param conditions the conditions - * @return cm handles returned from search. + * @param cmHandleQueryParameters the cm handle query parameters + * @return collection of cm handles */ @Override - public ResponseEntity executeCmHandleSearch(final Conditions conditions) { - final List conditionProperties = - conditions.getConditions().stream().collect(Collectors.toList()); - final CmHandles cmHandles = new CmHandles(); - cmHandles.setCmHandles(toCmHandleProperties(processConditions(conditionProperties))); - return ResponseEntity.ok(cmHandles); + public ResponseEntity> searchCmHandles( + final CmHandleQueryParameters cmHandleQueryParameters) { + final CmHandleQueryApiParameters cmHandleQueryApiParameters = + deprecationHelper.mapOldConditionProperties(cmHandleQueryParameters); + final Set cmHandles = networkCmProxyDataService + .executeCmHandleSearch(cmHandleQueryApiParameters); + final List outputCmHandles = + cmHandles.stream().map(this::toRestOutputCmHandle).collect(Collectors.toList()); + return ResponseEntity.ok(outputCmHandles); } /** - * Query and return cm handles that match the given query parameters. + * Query and return cm handle ids that match the given query parameters. * - * @param cmHandleQueryRestParameters the cm handle query parameters + * @param cmHandleQueryParameters the cm handle query parameters * @return collection of cm handle ids */ - public ResponseEntity> queryCmHandles( - final CmHandleQueryRestParameters cmHandleQueryRestParameters) { - final Set cmHandleIds = networkCmProxyDataService.queryCmHandles( - jsonObjectMapper.convertToValueType(cmHandleQueryRestParameters, CmHandleQueryApiParameters.class)); + @Override + public ResponseEntity> searchCmHandleIds( + final CmHandleQueryParameters cmHandleQueryParameters) { + final CmHandleQueryApiParameters cmHandleQueryApiParameters = + jsonObjectMapper.convertToValueType(cmHandleQueryParameters, CmHandleQueryApiParameters.class); + final Set cmHandleIds = networkCmProxyDataService.executeCmHandleIdSearch(cmHandleQueryApiParameters); return ResponseEntity.ok(List.copyOf(cmHandleIds)); } @@ -281,41 +279,6 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { return new ResponseEntity<>(restModuleReferences, HttpStatus.OK); } - private Collection processConditions(final List conditionProperties) { - for (final ConditionProperties conditionProperty : conditionProperties) { - if (conditionProperty.getName().equals("hasAllModules")) { - return executeCmHandleSearchesForModuleNames(conditionProperty); - } else { - log.warn("Unrecognized condition name {}.", conditionProperty.getName()); - } - } - log.warn("No valid conditions found {}.", conditionProperties); - return Collections.emptyList(); - } - - private Collection executeCmHandleSearchesForModuleNames(final ConditionProperties conditionProperties) { - return networkCmProxyDataService - .executeCmHandleHasAllModulesSearch(getModuleNames(conditionProperties.getConditionParameters())); - } - - private Collection getModuleNames(final ModuleNamesAsJsonArray moduleNamesAsJsonArray) { - final Collection moduleNames = new ArrayList<>(moduleNamesAsJsonArray.size()); - for (final ModuleNameAsJsonObject moduleNameAsJsonObject : moduleNamesAsJsonArray) { - moduleNames.add(moduleNameAsJsonObject.getModuleName()); - } - return moduleNames; - } - - private CmHandleProperties toCmHandleProperties(final Collection cmHandleIdentifiers) { - final CmHandleProperties cmHandleProperties = new CmHandleProperties(); - for (final String cmHandleIdentifier : cmHandleIdentifiers) { - final CmHandleProperty cmHandleProperty = new CmHandleProperty(); - cmHandleProperty.setCmHandleId(cmHandleIdentifier); - cmHandleProperties.add(cmHandleProperty); - } - return cmHandleProperties; - } - private RestOutputCmHandle toRestOutputCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle) { final RestOutputCmHandle restOutputCmHandle = new RestOutputCmHandle(); final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties(); diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DeprecationHelper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DeprecationHelper.java new file mode 100644 index 0000000000..fc992da41a --- /dev/null +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DeprecationHelper.java @@ -0,0 +1,73 @@ +/* + * ============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.util; + +import java.util.ArrayList; +import java.util.Collections; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; +import org.onap.cps.ncmp.api.models.ConditionApiProperties; +import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class DeprecationHelper { + + private final JsonObjectMapper jsonObjectMapper; + + /** + * Convert the old condition properties to the new schema. + * !!! remove it after the old condition removed !!! + * it only works for module names + * + * @param cmHandleQueryParameters the original input parameter + */ + @Deprecated //this method wil be removed in Release 12 (No Name know yet) + public CmHandleQueryApiParameters mapOldConditionProperties( + final CmHandleQueryParameters cmHandleQueryParameters) { + final CmHandleQueryApiParameters cmHandleQueryApiParameters = + jsonObjectMapper.convertToValueType(cmHandleQueryParameters, CmHandleQueryApiParameters.class); + if (cmHandleQueryParameters.getConditions() != null + && cmHandleQueryApiParameters.getCmHandleQueryParameters().isEmpty()) { + cmHandleQueryApiParameters.setCmHandleQueryParameters(new ArrayList<>()); + cmHandleQueryParameters.getConditions().parallelStream().forEach( + oldConditionProperty -> { + if (oldConditionProperty.getConditionParameters() != null + && oldConditionProperty.getName() != null) { + final ConditionApiProperties conditionApiProperties = new ConditionApiProperties(); + conditionApiProperties.setConditionName(oldConditionProperty.getName()); + conditionApiProperties.setConditionParameters(new ArrayList<>()); + oldConditionProperty.getConditionParameters().parallelStream().forEach( + oldConditionParameter -> + conditionApiProperties.getConditionParameters().add(Collections + .singletonMap("moduleName", oldConditionParameter.getModuleName())) + ); + cmHandleQueryApiParameters.getCmHandleQueryParameters().add(conditionApiProperties); + } + } + ); + } + + return cmHandleQueryApiParameters; + } +} 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 60ea736d72..036928fe35 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 @@ -29,6 +29,7 @@ import org.onap.cps.ncmp.api.inventory.CompositeState import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor +import org.onap.cps.ncmp.rest.util.DeprecationHelper import spock.lang.Shared import java.time.OffsetDateTime @@ -86,6 +87,9 @@ class NetworkCmProxyControllerSpec extends Specification { @SpringBean CpsNcmpTaskExecutor spiedCpsTaskExecutor = Spy() + @SpringBean + DeprecationHelper stubbedDeprecationHelper = Stub() + @Value('${rest.api.ncmp-base-path}/v1') def ncmpBasePathV1 @@ -239,8 +243,14 @@ class NetworkCmProxyControllerSpec extends Specification { given: 'an endpoint and json data' def searchesEndpoint = "$ncmpBasePathV1/ch/searches" String jsonString = TestUtils.getResourceFileContent('cmhandle-search.json') - and: 'the service method is invoked with module names and returns two cm handle ids' - mockNetworkCmProxyDataService.executeCmHandleHasAllModulesSearch(['module1', 'module2']) >> ['some-cmhandle-id1', 'some-cmhandle-id2'] + and: 'the service method is invoked with module names and returns two cm handles' + def cmHandle1 = new NcmpServiceCmHandle() + cmHandle1.cmHandleId = 'some-cmhandle-id1' + cmHandle1.publicProperties = [color:'yellow'] + def cmHandle2 = new NcmpServiceCmHandle() + cmHandle2.cmHandleId = 'some-cmhandle-id2' + cmHandle2.publicProperties = [color:'green'] + mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandle1, cmHandle2] when: 'the searches api is invoked' def response = mvc.perform(post(searchesEndpoint) .contentType(MediaType.APPLICATION_JSON) @@ -248,7 +258,7 @@ class NetworkCmProxyControllerSpec extends Specification { then: 'response status returns OK' response.status == HttpStatus.OK.value() and: 'the expected response content is returned' - response.contentAsString == '{"cmHandles":[{"cmHandleId":"some-cmhandle-id1"},{"cmHandleId":"some-cmhandle-id2"}]}' + response.contentAsString == '[{"cmHandle":"some-cmhandle-id1","publicCmHandleProperties":[{"color":"yellow"}],"state":null},{"cmHandle":"some-cmhandle-id2","publicCmHandleProperties":[{"color":"green"}],"state":null}]' } def 'Get Cm Handle details by Cm Handle id.'() { @@ -290,31 +300,38 @@ class NetworkCmProxyControllerSpec extends Specification { given: 'an endpoint and json data' def searchesEndpoint = "$ncmpBasePathV1/ch/searches" String jsonString = TestUtils.getResourceFileContent('invalid-cmhandle-search.json') + and: 'the service method is invoked with module names and returns two cm handles' + def cmHandel1 = new NcmpServiceCmHandle() + cmHandel1.cmHandleId = 'some-cmhandle-id1' + cmHandel1.publicProperties = [color:'yellow'] + def cmHandel2 = new NcmpServiceCmHandle() + cmHandel2.cmHandleId = 'some-cmhandle-id2' + cmHandel2.publicProperties = [color:'green'] + mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandel1, cmHandel2] when: 'the searches api is invoked' def response = mvc.perform(post(searchesEndpoint) .contentType(MediaType.APPLICATION_JSON) .content(jsonString)).andReturn().response then: 'an empty cm handle identifier is returned' - response.contentAsString == '{"cmHandles":[]}' + response.contentAsString == '[{"cmHandle":"some-cmhandle-id1","publicCmHandleProperties":[{"color":"yellow"}],"state":null},{"cmHandle":"some-cmhandle-id2","publicCmHandleProperties":[{"color":"green"}],"state":null}]' } 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"}}' + def searchesEndpoint = "$ncmpBasePathV1/ch/id-searches" and: 'the service method is invoked with module names and returns cm handle ids' - 1 * mockNetworkCmProxyDataService.queryCmHandles(_) >> ['some-cmhandle-id1', 'some-cmhandle-id2'] + 1 * mockNetworkCmProxyDataService.executeCmHandleIdSearch(_) >> ['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 + .content('{}')).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 searchesEndpoint = "$ncmpBasePathV1/ch/id-searches" def invalidInputData = '{invalidJson}' def response = mvc.perform(post(searchesEndpoint) .contentType(MediaType.APPLICATION_JSON) 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 45ed3d3070..1563c75b3f 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 @@ -31,6 +31,7 @@ import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException import org.onap.cps.ncmp.rest.controller.NcmpRestInputMapper import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor +import org.onap.cps.ncmp.rest.util.DeprecationHelper import org.onap.cps.spi.exceptions.CpsException import org.onap.cps.spi.exceptions.DataNodeNotFoundException import org.onap.cps.spi.exceptions.DataValidationException @@ -47,7 +48,6 @@ import spock.lang.Specification import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMP import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY -import static org.springframework.http.HttpStatus.BAD_GATEWAY import static org.springframework.http.HttpStatus.BAD_REQUEST import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR import static org.springframework.http.HttpStatus.NOT_FOUND @@ -75,6 +75,9 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { @SpringBean CpsNcmpTaskExecutor stubbedCpsTaskExecutor = Stub() + @SpringBean + DeprecationHelper stubbedDeprecationHelper = Stub() + @Value('${rest.api.ncmp-base-path}') def basePathNcmp diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/DeprecationHelperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/DeprecationHelperSpec.groovy new file mode 100644 index 0000000000..8c212d3531 --- /dev/null +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/DeprecationHelperSpec.groovy @@ -0,0 +1,54 @@ +/* + * ============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.util + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters +import org.onap.cps.ncmp.api.models.ConditionApiProperties +import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters +import org.onap.cps.ncmp.rest.model.ConditionProperties +import org.onap.cps.ncmp.rest.model.ModuleNameAsJsonObject +import org.onap.cps.ncmp.rest.model.OldConditionProperties +import org.onap.cps.utils.JsonObjectMapper +import spock.lang.Specification + +class DeprecationHelperSpec extends Specification { + + DeprecationHelper deprecationHelper = new DeprecationHelper(new JsonObjectMapper(new ObjectMapper())) + + def 'Map deprecated condition properties - #scenario.'() { + given: 'a deprecated condition properties' + def cmHandleQueryParameters = new CmHandleQueryParameters() + cmHandleQueryParameters.conditions = oldConditionPropertiesArray + cmHandleQueryParameters.cmHandleQueryParameters = cmHandleQueryParametersArray + when: 'converted into the new format' + def result = deprecationHelper.mapOldConditionProperties(cmHandleQueryParameters) + then: 'result is the expected' + assert result == new CmHandleQueryApiParameters(cmHandleQueryParameters: expectedCmHandleQueryApiParametersArray) + where: + scenario | oldConditionPropertiesArray | cmHandleQueryParametersArray || expectedCmHandleQueryApiParametersArray + 'mapping old query' | [new OldConditionProperties(name: 'hasAllModule', conditionParameters: [new ModuleNameAsJsonObject(moduleName: 'module-1')])] | [] || [new ConditionApiProperties(conditionName: 'hasAllModule', conditionParameters: [[moduleName:'module-1']])] + 'old condition is null' | null | [] || [] + 'old condition parameters is null' | [new OldConditionProperties(name: 'hasAllModule', conditionParameters: null)] | [] || [] + 'old condition name is null' | [new OldConditionProperties(name: null, conditionParameters: [new ModuleNameAsJsonObject(moduleName: 'module-1')])] | [] || [] + 'new query parameters are filled' | [new OldConditionProperties(name: 'hasAllModule', conditionParameters: [new ModuleNameAsJsonObject(moduleName: 'module-1')])] | [new ConditionProperties(conditionName: 'hasAllModule', conditionParameters: [[moduleName:'module-2']])] || [new ConditionApiProperties(conditionName: 'hasAllModule', conditionParameters: [[moduleName:'module-2']])] + } +} -- cgit 1.2.3-korg