diff options
53 files changed, 697 insertions, 281 deletions
diff --git a/checkstyle/pom.xml b/checkstyle/pom.xml index ef7c09fe1..07e6cf966 100644 --- a/checkstyle/pom.xml +++ b/checkstyle/pom.xml @@ -25,7 +25,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.onap.cps</groupId> <artifactId>checkstyle</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <properties> <nexusproxy>https://nexus.onap.org</nexusproxy> diff --git a/cps-application/pom.xml b/cps-application/pom.xml index 24e41c0cd..50b06b2e6 100755 --- a/cps-application/pom.xml +++ b/cps-application/pom.xml @@ -27,7 +27,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-bom/pom.xml b/cps-bom/pom.xml index 7da4abf36..3e5f70d77 100644 --- a/cps-bom/pom.xml +++ b/cps-bom/pom.xml @@ -25,7 +25,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.onap.cps</groupId> <artifactId>cps-bom</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <packaging>pom</packaging> <description>This artifact contains dependencyManagement declarations of all published CPS components.</description> diff --git a/cps-dependencies/pom.xml b/cps-dependencies/pom.xml index 3d15f0e9a..5c2ff565e 100755 --- a/cps-dependencies/pom.xml +++ b/cps-dependencies/pom.xml @@ -23,7 +23,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.onap.cps</groupId> <artifactId>cps-dependencies</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>${project.groupId}:${project.artifactId}</name> @@ -44,6 +44,7 @@ <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath> <sonar.skip>true</sonar.skip> <testcontainers.version>1.15.1</testcontainers.version> + <mapstruct.version>1.4.2.Final</mapstruct.version> </properties> <distributionManagement> @@ -183,6 +184,16 @@ <artifactId>log4j-to-slf4j</artifactId> <version>2.17.1</version> </dependency> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct</artifactId> + <version>${mapstruct.version}</version> + </dependency> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct-processor</artifactId> + <version>${mapstruct.version}</version> + </dependency> </dependencies> </dependencyManagement> </project> diff --git a/cps-events/pom.xml b/cps-events/pom.xml index 4327a3aa9..b9b399c95 100644 --- a/cps-events/pom.xml +++ b/cps-events/pom.xml @@ -24,7 +24,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml index cda6ca3ac..fd02b6e56 100644 --- a/cps-ncmp-rest/docs/openapi/components.yaml +++ b/cps-ncmp-rest/docs/openapi/components.yaml @@ -38,12 +38,15 @@ components: dmiPlugin: type: string example: my-dmi-plugin + default: "" dmiDataPlugin: type: string example: my-dmi-data-plugin + default: "" dmiModelPlugin: type: string example: my-dmi-model-plugin + default: "" createdCmHandles: type: array items: diff --git a/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml b/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml index f3f84fed9..3cd8e8baf 100755 --- a/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml +++ b/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml @@ -31,8 +31,8 @@ updateDmiRegistration: schema: $ref: 'components.yaml#/components/schemas/RestDmiPluginRegistration' responses: - 201: - $ref: 'components.yaml#/components/responses/Created' + 204: + $ref: 'components.yaml#/components/responses/NoContent' 400: $ref: 'components.yaml#/components/responses/BadRequest' 401: diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml index 26b83bef8..97305cfe9 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>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> @@ -61,6 +61,14 @@ <groupId>io.swagger.core.v3</groupId> <artifactId>swagger-annotations</artifactId> </dependency> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct</artifactId> + </dependency> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct-processor</artifactId> + </dependency> <!-- T E S T D E P E N D E N C I E S --> <dependency> <groupId>org.codehaus.groovy</groupId> 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 3b72cec38..36991952c 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,12 +1,14 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Bell Canada + * Modifications 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. @@ -19,11 +21,9 @@ package org.onap.cps.ncmp.rest.controller; - -import com.fasterxml.jackson.databind.ObjectMapper; import javax.validation.Valid; +import lombok.RequiredArgsConstructor; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; -import org.onap.cps.ncmp.api.models.DmiPluginRegistration; import org.onap.cps.ncmp.rest.api.NetworkCmProxyInventoryApi; import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration; import org.springframework.http.HttpStatus; @@ -33,21 +33,11 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("${rest.api.ncmp-inventory-base-path}") +@RequiredArgsConstructor public class NetworkCmProxyInventoryController implements NetworkCmProxyInventoryApi { private final NetworkCmProxyDataService networkCmProxyDataService; - private final ObjectMapper objectMapper; - - /** - * Constructor Injection for Dependencies. - * @param networkCmProxyDataService Data Service Interface - * @param objectMapper Object Mapper - */ - public NetworkCmProxyInventoryController(final NetworkCmProxyDataService networkCmProxyDataService, - final ObjectMapper objectMapper) { - this.networkCmProxyDataService = networkCmProxyDataService; - this.objectMapper = objectMapper; - } + private final RestInputMapper restInputMapper; /** * Update DMI Plugin Registration (used for first registration also). @@ -56,15 +46,11 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor @Override public ResponseEntity<Void> updateDmiPluginRegistration( final @Valid RestDmiPluginRegistration restDmiPluginRegistration) { - final DmiPluginRegistration dmiPluginRegistration = - convertRestObjectToJavaApiObject(restDmiPluginRegistration); - networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration); - return new ResponseEntity<>(HttpStatus.CREATED); - } - - private DmiPluginRegistration convertRestObjectToJavaApiObject( - final RestDmiPluginRegistration restDmiPluginRegistration) { - return objectMapper.convertValue(restDmiPluginRegistration, DmiPluginRegistration.class); + networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + restInputMapper.toDmiPluginRegistration(restDmiPluginRegistration)); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } } + + diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/RestInputMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/RestInputMapper.java new file mode 100644 index 000000000..d38204cf1 --- /dev/null +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/RestInputMapper.java @@ -0,0 +1,46 @@ +/* + * ============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.controller; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.NullValueMappingStrategy; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.onap.cps.ncmp.api.models.CmHandle; +import org.onap.cps.ncmp.api.models.DmiPluginRegistration; +import org.onap.cps.ncmp.rest.model.RestCmHandle; +import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration; + +@Mapper(componentModel = "spring", nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT, + nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT) +public interface RestInputMapper { + + DmiPluginRegistration toDmiPluginRegistration(final RestDmiPluginRegistration restDmiPluginRegistration); + + @Mappings({ + @Mapping(source = "cmHandle", target = "cmHandleID"), + @Mapping(source = "cmHandleProperties", target = "dmiProperties"), + @Mapping(source = "publicCmHandleProperties", target = "publicProperties") + }) + CmHandle toCmHandle(final RestCmHandle restCmHandle); + +} 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 d3450e596..fd01096e1 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 @@ -27,10 +27,13 @@ import org.onap.cps.ncmp.api.impl.exception.DmiRequestException; import org.onap.cps.ncmp.api.impl.exception.NcmpException; import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException; import org.onap.cps.ncmp.rest.controller.NetworkCmProxyController; +import org.onap.cps.ncmp.rest.controller.NetworkCmProxyInventoryController; import org.onap.cps.ncmp.rest.model.ErrorMessage; import org.onap.cps.spi.exceptions.CpsException; +import org.onap.cps.spi.exceptions.DataValidationException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -38,7 +41,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; * Exception handler with error message return. */ @Slf4j -@RestControllerAdvice(assignableTypes = {NetworkCmProxyController.class}) +@RestControllerAdvice(assignableTypes = {NetworkCmProxyController.class, NetworkCmProxyInventoryController.class}) @NoArgsConstructor(access = AccessLevel.PACKAGE) public class NetworkCmProxyRestExceptionHandler { @@ -71,6 +74,11 @@ public class NetworkCmProxyRestExceptionHandler { return buildErrorResponse(HttpStatus.BAD_REQUEST, exception); } + @ExceptionHandler({DataValidationException.class, HttpMessageNotReadableException.class}) + public static ResponseEntity<Object> handleDataValidatedExceptions(final Exception exception) { + return buildErrorResponse(HttpStatus.BAD_REQUEST, exception); + } + private static ResponseEntity<Object> buildErrorResponse(final HttpStatus status, final Exception exception) { if (exception.getCause() != null || !(exception instanceof CpsException)) { log.error("Exception occurred", exception); 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 8d434e775..079554a22 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 @@ -8,6 +8,7 @@ * 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. @@ -23,8 +24,9 @@ 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.CmHandle import org.onap.cps.ncmp.api.models.DmiPluginRegistration +import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration +import org.onap.cps.utils.JsonObjectMapper import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value @@ -46,46 +48,51 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { @SpringBean NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock() + @SpringBean + RestInputMapper restInputMapper = Mock() + + DmiPluginRegistration mockDmiPluginRegistration = Mock() + + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + @Value('${rest.api.ncmp-inventory-base-path}/v1') def ncmpBasePathV1 - def 'Register CM Handle Event' () { - given: 'jsonData' - def jsonData = TestUtils.getResourceFileContent('dmi-registration.json') - when: 'post request is performed' + 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' + def expectedRestDmiPluginRegistration = jsonObjectMapper.convertJsonString(jsonData, RestDmiPluginRegistration) + and: 'the converter returns a dmi registration (only for the expected input object)' + restInputMapper.toDmiPluginRegistration(expectedRestDmiPluginRegistration) >> mockDmiPluginRegistration + when: 'post request is performed & registration is called with correct DMI plugin information' def response = mvc.perform( post("$ncmpBasePathV1/ch") - .contentType(MediaType.APPLICATION_JSON) - .content(jsonData) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonData) ).andReturn().response - then: 'the cm handles are registered with the service' - 1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(_) - and: 'response status is created' - response.status == HttpStatus.CREATED.value() + then: 'the converted object is forwarded to the registration service' + 1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(mockDmiPluginRegistration) + and: 'response status is no content' + response.status == HttpStatus.NO_CONTENT.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' + 'updated cm handle with updated/new and removed properties' | 'dmi_registration_updates_only.json' + 'without any properties' | 'dmi_registration_without_properties.json' } - def 'Dmi plugin registration with #scenario' () { - given: 'jsonData, cmHandle, & DmiPluginRegistration' - def jsonData = TestUtils.getResourceFileContent('dmi_registration_combined_valid.json' ) - def cmHandle = new CmHandle(cmHandleID : 'example-name') - def expectedDmiPluginRegistration = new DmiPluginRegistration( - dmiPlugin: 'service1', - dmiDataPlugin: '', - dmiModelPlugin: '', - createdCmHandles: [cmHandle]) + 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' def response = mvc.perform( post("$ncmpBasePathV1/ch") .contentType(MediaType.APPLICATION_JSON) - .content(jsonData) + .content(jsonDataWithUndefinedDataLabel) ).andReturn().response - then: 'no NcmpException is thrown & updateDmiRegistrationAndSyncModule is called with correct parameters' - 1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule({ - it.getDmiPlugin() == expectedDmiPluginRegistration.getDmiPlugin() - it.getDmiDataPlugin() == expectedDmiPluginRegistration.getDmiDataPlugin() - it.getDmiModelPlugin() == expectedDmiPluginRegistration.getDmiModelPlugin() - it.getCreatedCmHandles().get(0).getCmHandleID() == expectedDmiPluginRegistration.getCreatedCmHandles().get(0).getCmHandleID() - }) + then: 'response status is bad request' + response.status == HttpStatus.BAD_REQUEST.value() } -} +} diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/RestInputMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/RestInputMapperSpec.groovy new file mode 100644 index 000000000..c48a8ed36 --- /dev/null +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/RestInputMapperSpec.groovy @@ -0,0 +1,64 @@ +/* + * ============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.controller + +import org.mapstruct.factory.Mappers +import org.onap.cps.ncmp.rest.model.RestCmHandle +import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration +import spock.lang.Specification + +class RestInputMapperSpec extends Specification { + + def objectUnderTest = Mappers.getMapper(RestInputMapper.class) + + def 'Convert a created REST CM Handle Input to an NCMP Service CM Handle with #scenario'() { + given: 'a rest cm handle input' + def inputRestCmHandle = new RestCmHandle(cmHandle : 'example-id', cmHandleProperties: dmiProperties, + publicCmHandleProperties: publicProperties) + def restDmiPluginRegistration = new RestDmiPluginRegistration( + createdCmHandles: [inputRestCmHandle]) + when: 'to plugin dmi registration is called' + def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration) + 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' + and: '(empty) properties are converted correctly' + result.createdCmHandles[0].dmiProperties == expectedDmiProperties + result.createdCmHandles[0].publicProperties == expectedPublicProperties + where: 'the following parameters are used' + scenario | dmiProperties | publicProperties || expectedDmiProperties | expectedPublicProperties + 'dmi and public properties' | ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property'] || ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property'] + 'no properties' | null | null || [:] | [:] + } + + def 'Handling empty dmi registration'() { + given: 'a rest cm handle input without any cm handles' + def restDmiPluginRegistration = new RestDmiPluginRegistration() + when: 'to plugin dmi registration is called' + def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration) + then: 'unspecified lists remain as empty lists' + assert result.createdCmHandles == [] + assert result.updatedCmHandles == [] + assert result.removedCmHandles == [] + } + +} 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 1b72b8c08..306f546f3 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 @@ -8,6 +8,7 @@ * 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. @@ -22,22 +23,29 @@ package org.onap.cps.ncmp.rest.exceptions import groovy.json.JsonSlurper import org.modelmapper.ModelMapper +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.ServerNcmpException +import org.onap.cps.ncmp.rest.controller.RestInputMapper import org.onap.cps.spi.exceptions.CpsException +import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.utils.JsonObjectMapper 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.MediaType import org.springframework.test.web.servlet.MockMvc import spock.lang.Shared 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_REQUEST import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post @WebMvcTest class NetworkCmProxyRestExceptionHandlerSpec extends Specification { @@ -54,10 +62,17 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { @SpringBean JsonObjectMapper jsonObjectMapper = Stub() + @SpringBean + RestInputMapper restInputMapper = Mock() + @Value('${rest.api.ncmp-base-path}') - def basePath + def basePathNcmp - def dataNodeBaseEndpoint + @Value('${rest.api.ncmp-inventory-base-path}') + def basePathNcmpInventory + + def dataNodeBaseEndpointNcmp + def dataNodeBaseEndpointNcmpInventory @Shared def errorMessage = 'some error message' @@ -65,13 +80,14 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { def errorDetails = 'some error details' def setup() { - dataNodeBaseEndpoint = "$basePath/v1" + dataNodeBaseEndpointNcmp = "$basePathNcmp/v1" + dataNodeBaseEndpointNcmpInventory = "$basePathNcmpInventory/v1" } def 'Get request with generic #scenario exception returns correct HTTP Status.'() { when: 'an exception is thrown by the service' - setupTestException(exception) - def response = performTestRequest() + setupTestException(exception, NCMP) + def response = performTestRequest(NCMP) then: 'an HTTP response is returned with correct message and details' assertTestResponse(response, expectedErrorCode, errorMessage, expectedErrorDetails) where: @@ -82,13 +98,29 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { 'other' | new IllegalStateException(errorMessage) || null | INTERNAL_SERVER_ERROR } - def setupTestException(exception){ - mockNetworkCmProxyDataService.getYangResourcesModuleReferences('testCmHandle')>> - { throw exception} + def 'Post request with exception returns correct HTTP Status.'() { + given: 'the service throws data validation exception' + def exception = new DataValidationException(errorMessage, errorDetails) + setupTestException(exception, NCMPINVENTORY) + when: 'the HTTP request is made' + def response = performTestRequest(NCMPINVENTORY) + then: 'an HTTP response is returned with correct message and details' + assertTestResponse(response, BAD_REQUEST, errorMessage, errorDetails) } - def performTestRequest(){ - return mvc.perform(get("$dataNodeBaseEndpoint/ch/testCmHandle/modules")).andReturn().response + def setupTestException(exception, apiType) { + if (NCMP == apiType) { + mockNetworkCmProxyDataService.getYangResourcesModuleReferences(*_) >> { throw exception } + } + mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(*_) >> { throw exception } + } + + def performTestRequest(apiType) { + if (NCMP == apiType) { + return mvc.perform(get("$dataNodeBaseEndpointNcmp/ch/testCmHandle/modules")).andReturn().response + } + def jsonData = TestUtils.getResourceFileContent('dmi_registration_all_singing_and_dancing.json') + return mvc.perform(post("$dataNodeBaseEndpointNcmpInventory/ch").contentType(MediaType.APPLICATION_JSON).content(jsonData)).andReturn().response } static void assertTestResponse(response, expectedStatus , expectedErrorMessage , expectedErrorDetails) { @@ -98,4 +130,9 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { assert content['message'] == expectedErrorMessage assert expectedErrorDetails == null || content['details'] == expectedErrorDetails } + + enum ApiType { + NCMP, + NCMPINVENTORY; + } } diff --git a/cps-ncmp-rest/src/test/resources/dmi-registration.json b/cps-ncmp-rest/src/test/resources/dmi-registration.json deleted file mode 100644 index 4f27e1184..000000000 --- a/cps-ncmp-rest/src/test/resources/dmi-registration.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "dmiPlugin": "onap-dmi-plugin", - "createdCmHandles": [ - { - "cmHandle": "example-name", - "cmHandleProperties": { - "subSystemId" : "system-001" - }, - "publicCmHandleProperties": { - "area" : "south" - } - } - ] -}
\ No newline at end of file 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 new file mode 100644 index 000000000..fd8b56b02 --- /dev/null +++ b/cps-ncmp-rest/src/test/resources/dmi_registration_all_singing_and_dancing.json @@ -0,0 +1,43 @@ +{ + "dmiDataPlugin":"service2", + "dmiModelPlugin":"service3", + "createdCmHandles":[ + { + "cmHandle":"ch1(new)", + "cmHandleProperties":{ + "dmiProp1":"ch1-dmi1", + "dmiProp2":"ch1-dmi2" + }, + "publicCmHandleProperties":{ + "pubProp1":"ch1-pub1", + "pubProp2":"ch1-pub2" + } + }, + { + "cmHandle":"ch2(new)", + "cmHandleProperties":{ + "dmiProp1":"ch2-dmi1", + "dmiProp2":"ch2-dmi2" + }, + "publicCmHandleProperties":{ + "pubProp1":"ch2-pub1", + "pubProp2":"ch2-pub2" + } + } + ], + "updatedCmHandles":[ + { + "cmHandle":"ch3(upd)", + "cmHandleProperties":{ + "dmiProp1":"ch3-dmi1" + }, + "publicCmHandleProperties":{ + "pubProp2":"ch3-pub1" + } + } + ], + "removedCmHandles":[ + "ch4", + "ch5" + ] +} diff --git a/cps-ncmp-rest/src/test/resources/dmi_registration_combined_valid.json b/cps-ncmp-rest/src/test/resources/dmi_registration_combined_valid.json deleted file mode 100644 index bded4f215..000000000 --- a/cps-ncmp-rest/src/test/resources/dmi_registration_combined_valid.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "dmiPlugin": "service1", - "dmiDataPlugin": "", - "dmiModelPlugin": "", - "createdCmHandles": [{ - "cmHandle": "example-name" - }] -}
\ No newline at end of file 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 new file mode 100644 index 000000000..58a1a9836 --- /dev/null +++ b/cps-ncmp-rest/src/test/resources/dmi_registration_updates_only.json @@ -0,0 +1,12 @@ +{ + "dmiPlugin": "service1", + "updatedCmHandles":[ + { + "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 new file mode 100644 index 000000000..395c098d2 --- /dev/null +++ b/cps-ncmp-rest/src/test/resources/dmi_registration_without_properties.json @@ -0,0 +1,10 @@ +{ + "dmiPlugin":"", + "dmiDataPlugin":"service2", + "dmiModelPlugin":"service3", + "createdCmHandles":[ + { + "cmHandle": "ch1(new)" + } + ] +} diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index 871bd14c5..5145a12ce 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -26,7 +26,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index 38f8e1707..446e45b2b 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -32,11 +32,11 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED; import com.fasterxml.jackson.core.JsonProcessingException; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsAdminService; @@ -81,19 +81,17 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService public void updateDmiRegistrationAndSyncModule(final DmiPluginRegistration dmiPluginRegistration) { dmiPluginRegistration.validateDmiPluginRegistration(); try { - if (dmiPluginRegistration.getCreatedCmHandles() != null) { + if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) { parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration); } - if (dmiPluginRegistration.getUpdatedCmHandles() != null) { + if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) { parseAndUpdateCmHandlesInDmiRegistration(dmiPluginRegistration); } - if (dmiPluginRegistration.getRemovedCmHandles() != null) { - parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration); - } + parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration); } catch (final JsonProcessingException | DataNodeNotFoundException e) { final String errorMessage = String.format( - "Error occurred while processing the CM-handle registration request [%s] ,caused by : [%s]", - dmiPluginRegistration, e.getMessage()); + "Error occurred while processing the CM-handle registration request, caused by : [%s]", + e.getMessage()); throw new DataValidationException(errorMessage, e.getMessage(), e); } } @@ -228,38 +226,27 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } private void syncAndCreateSchemaSet(final PersistenceCmHandle persistenceCmHandle) { - final List<ModuleReference> moduleReferencesFromCmHandle = + final Collection<ModuleReference> moduleReferencesFromCmHandle = dmiModelOperations.getModuleReferences(persistenceCmHandle); - final List<ModuleReference> existingModuleReferences = new ArrayList<>(); - final List<ModuleReference> unknownModuleReferences = new ArrayList<>(); - prepareModuleSubsets(moduleReferencesFromCmHandle, existingModuleReferences, unknownModuleReferences); - final Map<String, String> newYangResourcesModuleNameToContentMap; - if (unknownModuleReferences.isEmpty()) { - newYangResourcesModuleNameToContentMap = new HashMap<>(); + final Collection<ModuleReference> identifiedNewModuleReferencesFromCmHandle = cpsModuleService + .identifyNewModuleReferences(moduleReferencesFromCmHandle); + + final Collection<ModuleReference> existingModuleReferencesFromCmHandle = + moduleReferencesFromCmHandle.stream().filter(moduleReferenceFromCmHandle -> + !identifiedNewModuleReferencesFromCmHandle.contains(moduleReferenceFromCmHandle) + ).collect(Collectors.toList()); + + final Map<String, String> newModuleNameToContentMap; + if (identifiedNewModuleReferencesFromCmHandle.isEmpty()) { + newModuleNameToContentMap = new HashMap<>(); } else { - newYangResourcesModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle, - unknownModuleReferences); + newModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle, + identifiedNewModuleReferencesFromCmHandle); } cpsModuleService .createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, persistenceCmHandle.getId(), - newYangResourcesModuleNameToContentMap, existingModuleReferences); - } - - private void prepareModuleSubsets(final List<ModuleReference> moduleReferencesFromCmHandle, - final List<ModuleReference> existingModuleReferences, - final List<ModuleReference> unknownModuleReferences) { - - final Collection<ModuleReference> knownModuleReferencesInCps = - cpsModuleService.getYangResourceModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME); - - for (final ModuleReference moduleReferenceFromDmiForCmHandle : moduleReferencesFromCmHandle) { - if (knownModuleReferencesInCps.contains(moduleReferenceFromDmiForCmHandle)) { - existingModuleReferences.add(moduleReferenceFromDmiForCmHandle); - } else { - unknownModuleReferences.add(moduleReferenceFromDmiForCmHandle); - } - } + newModuleNameToContentMap, existingModuleReferencesFromCmHandle); } private void createAnchor(final PersistenceCmHandle persistenceCmHandle) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java index 657b0b474..aec4517c0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java @@ -25,6 +25,7 @@ import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.MODEL; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -76,13 +77,13 @@ public class DmiModelOperations extends DmiOperations { * Retrieve yang resources from dmi for any modules that CPS-NCMP hasn't cached before. * * @param persistenceCmHandle the persistenceCmHandle - * @param unknownModuleReferences the unknown module references + * @param newModuleReferences the unknown module references * @return yang resources as map of module name to yang(re)source */ public Map<String, String> getNewYangResourcesFromDmi(final PersistenceCmHandle persistenceCmHandle, - final List<ModuleReference> unknownModuleReferences) { + final Collection<ModuleReference> newModuleReferences) { final String jsonWithDataAndDmiProperties = getRequestBodyToFetchYangResources( - unknownModuleReferences, persistenceCmHandle.getDmiProperties()); + newModuleReferences, persistenceCmHandle.getDmiProperties()); final ResponseEntity<Object> responseEntity = getResourceFromDmiWithJsonData( persistenceCmHandle.resolveDmiServiceName(MODEL), jsonWithDataAndDmiProperties, @@ -108,9 +109,9 @@ public class DmiModelOperations extends DmiOperations { return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, new HttpHeaders()); } - private static String getRequestBodyToFetchYangResources(final List<ModuleReference> unknownModuleReferences, + private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences, final List<PersistenceCmHandle.Property> dmiProperties) { - final JsonArray moduleReferencesAsJson = getModuleReferencesAsJson(unknownModuleReferences); + final JsonArray moduleReferencesAsJson = getModuleReferencesAsJson(newModuleReferences); final JsonObject data = new JsonObject(); data.add("modules", moduleReferencesAsJson); final JsonObject jsonRequestObject = new JsonObject(); @@ -119,7 +120,7 @@ public class DmiModelOperations extends DmiOperations { return jsonRequestObject.toString(); } - private static JsonArray getModuleReferencesAsJson(final List<ModuleReference> unknownModuleReferences) { + private static JsonArray getModuleReferencesAsJson(final Collection<ModuleReference> unknownModuleReferences) { final JsonArray moduleReferences = new JsonArray(); for (final ModuleReference moduleReference : unknownModuleReferences) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java index 9a9b6faaf..88c6f3376 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java @@ -20,7 +20,6 @@ package org.onap.cps.ncmp.api.models; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.Nulls; import java.util.Collections; @@ -39,15 +38,12 @@ import org.springframework.validation.annotation.Validated; @NoArgsConstructor public class CmHandle { - @JsonProperty("cmHandle") private String cmHandleID; @JsonSetter(nulls = Nulls.AS_EMPTY) - @JsonProperty("cmHandleProperties") private Map<String, String> dmiProperties = Collections.emptyMap(); @JsonSetter(nulls = Nulls.AS_EMPTY) - @JsonProperty("publicCmHandleProperties") private Map<String, String> publicProperties = Collections.emptyMap(); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java index f1b3888c6..c302f7de5 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java @@ -22,9 +22,8 @@ package org.onap.cps.ncmp.api.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.annotation.Nulls; import com.google.common.base.Strings; +import java.util.Collections; import java.util.List; import lombok.Getter; import lombok.Setter; @@ -39,20 +38,17 @@ import org.onap.cps.ncmp.api.impl.exception.NcmpException; @JsonInclude(Include.NON_NULL) public class DmiPluginRegistration { - @JsonSetter(nulls = Nulls.AS_EMPTY) private String dmiPlugin; - @JsonSetter(nulls = Nulls.AS_EMPTY) private String dmiDataPlugin; - @JsonSetter(nulls = Nulls.AS_EMPTY) private String dmiModelPlugin; - private List<CmHandle> createdCmHandles; + private List<CmHandle> createdCmHandles = Collections.emptyList(); - private List<CmHandle> updatedCmHandles; + private List<CmHandle> updatedCmHandles = Collections.emptyList(); - private List<String> removedCmHandles; + private List<String> removedCmHandles = Collections.emptyList(); /** * Validates plugin service names. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java index d4f6e952d..9762ac4e1 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java @@ -30,7 +30,7 @@ import lombok.Getter; public class PersistenceCmHandlesList { @JsonProperty("cm-handles") - private List<PersistenceCmHandle> persistenceCmHandles = new ArrayList<>(); + private final List<PersistenceCmHandle> persistenceCmHandles = new ArrayList<>(); /** * Create a PersistenceCmHandleList given all service names and a collection of cmHandles. diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy index 3af4fc00e..3f82f5e0b 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy @@ -50,26 +50,36 @@ class NetworkCmProxyDataServiceImplModelSyncSpec extends Specification { def cmHandle = new CmHandle() def dmiServiceName = 'some service name' cmHandle.cmHandleID = 'cm handle id 1' - cmHandle.dmiProperties = dmiProperties def persistenceCmHandle = PersistenceCmHandle.toPersistenceCmHandle(dmiServiceName, '' , '', cmHandle) and: 'DMI operations returns some module references' def moduleReferences = [ new ModuleReference(moduleName:'module1',revision:'1'), new ModuleReference(moduleName:'module2',revision:'2') ] mockDmiModelOperations.getModuleReferences(persistenceCmHandle) >> moduleReferences and: 'CPS-Core returns list of existing module resources' - mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> existingModuleResourcesInCps + mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> toModuleReference(existingModuleResourcesInCps) and: 'DMI-Plugin returns resource(s) for "new" module(s)' mockDmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle, [new ModuleReference('module1', '1')]) >> yangResourceToContentMap when: 'module sync is triggered' + mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReference(identifiedNewModuleReferences) objectUnderTest.syncModulesAndCreateAnchor(persistenceCmHandle) then: 'the CPS module service is called once with the correct parameters' - 1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, persistenceCmHandle.getId(), yangResourceToContentMap, expectedKnownModules) + 1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, persistenceCmHandle.getId(), yangResourceToContentMap, toModuleReference(expectedKnownModules)) and: 'admin service create anchor method has been called with correct parameters' 1 * mockCpsAdminService.createAnchor(expectedDataspaceName, persistenceCmHandle.getId(), persistenceCmHandle.getId()) where: 'the following parameters are used' - scenario | dmiProperties | existingModuleResourcesInCps | yangResourceToContentMap || expectedKnownModules - 'one unknown module' | ['name1': 'value1'] | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')] - 'no add. properties' | [:] | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')] - 'no unknown module' | [:] | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | [:] || [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] + scenario | existingModuleResourcesInCps | identifiedNewModuleReferences | yangResourceToContentMap || expectedKnownModules + 'one new module' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']] | [module1: 'some yang source'] || [['module2' : '2']] + 'no add. properties' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']] | [module1: 'some yang source'] || [['module2' : '2']] + 'no new module' | [['module1' : '1'], ['module2' : '2']] | [] | [:] || [['module1' : '1'], ['module2' : '2']] } + + def toModuleReference(moduleReferenceAsMap) { + def moduleReferences = [].withDefault { [:] } + moduleReferenceAsMap.forEach(property -> + property.forEach((moduleName, revision) -> { + moduleReferences.add(new ModuleReference('moduleName' : moduleName, 'revision' : revision)) + })) + return moduleReferences + } + } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy index 00fda149f..a475f9c34 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy @@ -76,19 +76,19 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { expectedCallsToSaveNode * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData, noTimestamp) and: 'update data node leaves is called with correct parameters' - expectedCallsToPropertyHandler * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(updatedCmHandles) + expectedCallsToUpdateCmHandleProperty * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(updatedCmHandles) and: 'delete schema set is invoked with the correct parameters' expectedCallsToDeleteSchemaSetAndListElement * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'cmHandle001', CASCADE_DELETE_ALLOWED) and: 'delete list or list element is invoked with the correct parameters' expectedCallsToDeleteSchemaSetAndListElement * mockCpsDataService.deleteListOrListElement('NCMP-Admin', 'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp) where: - scenario | createdCmHandles | updatedCmHandles | removedCmHandles || expectedCallsToSaveNode | expectedCallsToDeleteSchemaSetAndListElement | expectedCallsToPropertyHandler - 'create' | [persistenceCmHandle] | [] | [] || 1 | 0 | 1 + scenario | createdCmHandles | updatedCmHandles | removedCmHandles || expectedCallsToSaveNode | expectedCallsToDeleteSchemaSetAndListElement | expectedCallsToUpdateCmHandleProperty + 'create' | [persistenceCmHandle] | [] | [] || 1 | 0 | 0 'update' | [] | [persistenceCmHandle] | [] || 0 | 0 | 1 - 'delete' | [] | [] | cmHandlesArray || 0 | 1 | 1 + 'delete' | [] | [] | cmHandlesArray || 0 | 1 | 0 'create, update and delete' | [persistenceCmHandle] | [persistenceCmHandle] | cmHandlesArray || 1 | 1 | 1 - 'no valid data' | null | null | null || 0 | 0 | 0 + 'no valid data' | [] | [] | [] || 0 | 0 | 0 } def 'Register a DMI Plugin for the given cm-handle(s) without DMI properties.'() { @@ -107,22 +107,17 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { '/dmi-registry', expectedJsonData, noTimestamp) } - def 'Register a DMI Plugin for a given cm-handle(s) with JSON processing errors during #scenario process.'() { + def 'Register a DMI Plugin for a given cm-handle(s) with JSON processing errors during process.'() { given: 'a registration without cm-handle properties ' NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled() def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'some-plugin') - dmiPluginRegistration.createdCmHandles = createdCmHandles - dmiPluginRegistration.updatedCmHandles = updatedCmHandles + dmiPluginRegistration.createdCmHandles = [persistenceCmHandle] and: 'an json processing exception occurs' spiedJsonObjectMapper.asJsonString(_) >> { throw (new JsonProcessingException('')) } when: 'registration is updated and modules are synced' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'a data validation exception is thrown' thrown(DataValidationException) - where: - scenario | createdCmHandles | updatedCmHandles - 'create' | [persistenceCmHandle] | [] - 'update' | [] | [persistenceCmHandle] } def 'Register a DMI Plugin for the given cm-handle(s) with no data found during delete process.'() { diff --git a/cps-parent/pom.xml b/cps-parent/pom.xml index 2e0528e97..434464ac6 100755 --- a/cps-parent/pom.xml +++ b/cps-parent/pom.xml @@ -32,7 +32,7 @@ <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <packaging>pom</packaging> <properties> diff --git a/cps-path-parser/pom.xml b/cps-path-parser/pom.xml index 206f0094c..c8b88e8aa 100644 --- a/cps-path-parser/pom.xml +++ b/cps-path-parser/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-rest/docs/openapi/components.yml b/cps-rest/docs/openapi/components.yml index 5f31569c9..ae0326d7e 100644 --- a/cps-rest/docs/openapi/components.yml +++ b/cps-rest/docs/openapi/components.yml @@ -1,5 +1,5 @@ # ============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"); @@ -86,7 +86,7 @@ components: example: my-schema-set examples: - dataSampleRequest: + dataSample: value: test:bookstore: bookstore-name: Chapters @@ -96,16 +96,6 @@ components: - code: 02 name: kids - dataSampleResponse: - summary: The data node returned does not include the root node. This is being investigated as a part of CPS-461 - value: - bookstore-name: Chapters - categories: - - code: 01 - name: SciFi - - code: 02 - name: kids - parameters: dataspaceNameInQuery: name: dataspace-name @@ -269,7 +259,7 @@ components: schema: type: object examples: - dataSampleResponse: + dataSample: value: "" Created: description: Created diff --git a/cps-rest/docs/openapi/cpsData.yml b/cps-rest/docs/openapi/cpsData.yml index 099512d7d..265ee23ad 100644 --- a/cps-rest/docs/openapi/cpsData.yml +++ b/cps-rest/docs/openapi/cpsData.yml @@ -1,5 +1,5 @@ # ============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"); @@ -37,8 +37,8 @@ nodeByDataspaceAndAnchor: schema: type: object examples: - dataSampleResponse: - $ref: 'components.yml#/components/examples/dataSampleResponse' + dataSample: + $ref: 'components.yml#/components/examples/dataSample' '400': $ref: 'components.yml#/components/responses/BadRequest' '401': @@ -68,8 +68,8 @@ listElementByDataspaceAndAnchor: schema: type: object examples: - dataSampleRequest: - $ref: 'components.yml#/components/examples/dataSampleRequest' + dataSample: + $ref: 'components.yml#/components/examples/dataSample' responses: '201': $ref: 'components.yml#/components/responses/Created' @@ -99,8 +99,8 @@ listElementByDataspaceAndAnchor: schema: type: object examples: - dataSampleRequest: - $ref: 'components.yml#/components/examples/dataSampleRequest' + dataSample: + $ref: 'components.yml#/components/examples/dataSample' responses: '200': $ref: 'components.yml#/components/responses/Ok' @@ -155,8 +155,8 @@ nodesByDataspaceAndAnchor: schema: type: object examples: - dataSampleRequest: - $ref: 'components.yml#/components/examples/dataSampleRequest' + dataSample: + $ref: 'components.yml#/components/examples/dataSample' responses: '201': $ref: 'components.yml#/components/responses/Created' @@ -188,8 +188,8 @@ nodesByDataspaceAndAnchor: schema: type: object examples: - dataSampleRequest: - $ref: 'components.yml#/components/examples/dataSampleRequest' + dataSample: + $ref: 'components.yml#/components/examples/dataSample' responses: '200': $ref: 'components.yml#/components/responses/Ok' @@ -241,8 +241,8 @@ nodesByDataspaceAndAnchor: schema: type: object examples: - dataSampleRequest: - $ref: 'components.yml#/components/examples/dataSampleRequest' + dataSample: + $ref: 'components.yml#/components/examples/dataSample' responses: '200': $ref: 'components.yml#/components/responses/Ok' diff --git a/cps-rest/docs/openapi/cpsQuery.yml b/cps-rest/docs/openapi/cpsQuery.yml index 06c9ca2a2..dc0402d03 100644 --- a/cps-rest/docs/openapi/cpsQuery.yml +++ b/cps-rest/docs/openapi/cpsQuery.yml @@ -1,6 +1,6 @@ # ============LICENSE_START======================================================= # Copyright (C) 2021 Nordix Foundation -# Copyright (C) 2021-2022 Nordix Foundation +# 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. @@ -37,8 +37,8 @@ nodesByDataspaceAndAnchorAndCpsPath: schema: type: object examples: - dataSampleResponse: - $ref: 'components.yml#/components/examples/dataSampleResponse' + dataSample: + $ref: 'components.yml#/components/examples/dataSample' '400': $ref: 'components.yml#/components/responses/BadRequest' '401': diff --git a/cps-rest/pom.xml b/cps-rest/pom.xml index 0ca5fb6aa..20870c369 100755 --- a/cps-rest/pom.xml +++ b/cps-rest/pom.xml @@ -27,7 +27,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml index 8d860b004..37d93156a 100644 --- a/cps-ri/pom.xml +++ b/cps-ri/pom.xml @@ -26,7 +26,7 @@ <parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java index 86d5de6d0..ec720b8a9 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2020 Nordix Foundation + * Copyright (C) 2020-2022 Nordix Foundation * Modifications Copyright (C) 2020-2022 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech * ================================================================================ @@ -40,6 +40,7 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.transaction.Transactional; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; @@ -53,9 +54,8 @@ import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.DuplicatedYangResourceException; import org.onap.cps.spi.exceptions.ModelValidationException; import org.onap.cps.spi.model.ModuleReference; -import org.onap.cps.spi.repository.AnchorRepository; import org.onap.cps.spi.repository.DataspaceRepository; -import org.onap.cps.spi.repository.FragmentRepository; +import org.onap.cps.spi.repository.ModuleReferenceRepository; import org.onap.cps.spi.repository.SchemaSetRepository; import org.onap.cps.spi.repository.YangResourceRepository; import org.opendaylight.yangtools.yang.common.Revision; @@ -63,15 +63,14 @@ import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Component; - -@Component @Slf4j +@Component +@AllArgsConstructor public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceService { private static final String YANG_RESOURCE_CHECKSUM_CONSTRAINT_NAME = "yang_resource_checksum_key"; @@ -79,24 +78,16 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ private static final Pattern RFC6020_RECOMMENDED_FILENAME_PATTERN = Pattern .compile("([\\w-]+)@(\\d{4}-\\d{2}-\\d{2})(?:\\.yang)?", Pattern.CASE_INSENSITIVE); - @Autowired private YangResourceRepository yangResourceRepository; - @Autowired private SchemaSetRepository schemaSetRepository; - @Autowired private DataspaceRepository dataspaceRepository; - @Autowired - private AnchorRepository anchorRepository; - - @Autowired - private FragmentRepository fragmentRepository; - - @Autowired private CpsAdminPersistenceService cpsAdminPersistenceService; + private ModuleReferenceRepository moduleReferenceRepository; + @Override public Map<String, String> getYangSchemaResources(final String dataspaceName, final String schemaSetName) { final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName); @@ -137,9 +128,9 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ @Retryable(value = DuplicatedYangResourceException.class, maxAttempts = 5, backoff = @Backoff(random = true, delay = 200, maxDelay = 2000, multiplier = 2)) public void storeSchemaSet(final String dataspaceName, final String schemaSetName, - final Map<String, String> yangResourcesNameToContentMap) { + final Map<String, String> moduleReferenceNameToContentMap) { final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final var yangResourceEntities = synchronizeYangResources(yangResourcesNameToContentMap); + final var yangResourceEntities = synchronizeYangResources(moduleReferenceNameToContentMap); final var schemaSetEntity = new SchemaSetEntity(); schemaSetEntity.setName(schemaSetName); schemaSetEntity.setDataspace(dataspaceEntity); @@ -158,9 +149,9 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ @Retryable(value = DuplicatedYangResourceException.class, maxAttempts = 5, backoff = @Backoff(random = true, delay = 200, maxDelay = 2000, multiplier = 2)) public void storeSchemaSetFromModules(final String dataspaceName, final String schemaSetName, - final Map<String, String> newYangResourcesModuleNameToContentMap, - final List<ModuleReference> moduleReferences) { - storeSchemaSet(dataspaceName, schemaSetName, newYangResourcesModuleNameToContentMap); + final Map<String, String> newModuleNameToContentMap, + final Collection<ModuleReference> moduleReferences) { + storeSchemaSet(dataspaceName, schemaSetName, newModuleNameToContentMap); final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName); final var schemaSetEntity = schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName); @@ -186,8 +177,15 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ yangResourceRepository.deleteOrphans(); } - private Set<YangResourceEntity> synchronizeYangResources(final Map<String, String> yangResourcesNameToContentMap) { - final Map<String, YangResourceEntity> checksumToEntityMap = yangResourcesNameToContentMap.entrySet().stream() + @Override + public Collection<ModuleReference> identifyNewModuleReferences( + final Collection<ModuleReference> moduleReferencesToCheck) { + return moduleReferenceRepository.identifyNewModuleReferences(moduleReferencesToCheck); + } + + private Set<YangResourceEntity> synchronizeYangResources( + final Map<String, String> moduleReferenceNameToContentMap) { + final Map<String, YangResourceEntity> checksumToEntityMap = moduleReferenceNameToContentMap.entrySet().stream() .map(entry -> { final String checksum = DigestUtils.sha256Hex(entry.getValue().getBytes(StandardCharsets.UTF_8)); final Map<String, String> moduleNameAndRevisionMap = createModuleNameAndRevisionMap(entry.getKey(), diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java new file mode 100644 index 000000000..6551937e1 --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java @@ -0,0 +1,34 @@ +/*- + * ============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.spi.repository; + +import java.util.Collection; +import org.onap.cps.spi.model.ModuleReference; + +/** + * This interface is used in conjunction with {@link ModuleReferenceRepository} to create native sql queries. + */ +public interface ModuleReferenceQuery { + + Collection<ModuleReference> identifyNewModuleReferences( + final Collection<ModuleReference> moduleReferencesToCheck); + +} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepository.java new file mode 100644 index 000000000..ce2bfe784 --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepository.java @@ -0,0 +1,36 @@ +/* + * ============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.spi.repository; + +import java.util.Collection; +import org.onap.cps.spi.entities.YangResourceEntity; +import org.onap.cps.spi.model.ModuleReference; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ModuleReferenceRepository extends + JpaRepository<YangResourceEntity, Long>, ModuleReferenceQuery { + + Collection<ModuleReference> identifyNewModuleReferences( + final Collection<ModuleReference> moduleReferencesToCheck); + +}
\ No newline at end of file diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java new file mode 100644 index 000000000..f4078ffec --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java @@ -0,0 +1,107 @@ +/*- + * ============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.spi.repository; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.spi.model.ModuleReference; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Transactional +public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery { + + @PersistenceContext + private EntityManager entityManager; + + @Override + @SneakyThrows + public Collection<ModuleReference> identifyNewModuleReferences( + final Collection<ModuleReference> moduleReferencesToCheck) { + + if (moduleReferencesToCheck == null || moduleReferencesToCheck.isEmpty()) { + return Collections.EMPTY_LIST; + } + + final String tempTableName = "moduleReferencesToCheckTemp" + + UUID.randomUUID().toString().replaceAll("-", ""); + + createTemporaryTable(tempTableName); + insertDataIntoTable(tempTableName, moduleReferencesToCheck); + + return identifyNewModuleReferencesForCmHandle(tempTableName); + } + + private void createTemporaryTable(final String tempTableName) { + final StringBuilder sqlStringBuilder = new StringBuilder("CREATE TEMPORARY TABLE " + tempTableName + "("); + sqlStringBuilder.append(" id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,"); + sqlStringBuilder.append(" module_name varchar NOT NULL,"); + sqlStringBuilder.append(" revision varchar NOT NULL"); + sqlStringBuilder.append(");"); + + entityManager.createNativeQuery(sqlStringBuilder.toString()).executeUpdate(); + } + + private void insertDataIntoTable(final String tempTableName, final Collection<ModuleReference> moduleReferences) { + final StringBuilder sqlStringBuilder = new StringBuilder("INSERT INTO " + tempTableName); + sqlStringBuilder.append(" (module_name, revision) "); + sqlStringBuilder.append(" VALUES "); + + for (final ModuleReference moduleReference : moduleReferences) { + sqlStringBuilder.append("('"); + sqlStringBuilder.append(moduleReference.getModuleName()); + sqlStringBuilder.append("', '"); + sqlStringBuilder.append(moduleReference.getRevision()); + sqlStringBuilder.append("'),"); + } + + // replace last ',' with ';' + sqlStringBuilder.replace(sqlStringBuilder.length() - 1, sqlStringBuilder.length(), ";"); + + entityManager.createNativeQuery(sqlStringBuilder.toString()).executeUpdate(); + } + + private Collection<ModuleReference> identifyNewModuleReferencesForCmHandle(final String tempTableName) { + final String sql = String.format( + "SELECT %1$s.module_name, %1$s.revision" + + " FROM %1$s LEFT JOIN yang_resource" + + " ON yang_resource.module_name=%1$s.module_name" + + " AND yang_resource.revision=%1$s.revision" + + " WHERE yang_resource.module_name IS NULL;", tempTableName); + + final List<Object[]> resultsAsObjects = + entityManager.createNativeQuery(sql).getResultList(); + + final List<ModuleReference> resultsAsModuleReferences = new ArrayList<>(resultsAsObjects.size()); + for (final Object[] row : resultsAsObjects) { + resultsAsModuleReferences.add(new ModuleReference((String) row[0], (String) row[1])); + } + + return resultsAsModuleReferences; + } +} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java index 3f5c43dec..895937b60 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Pantheon.tech - * Modifications Copyright (C) Nordix Foundation + * Modifications Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy index 75d633026..1b37bef9c 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * Modifications Copyright (C) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the 'License'); @@ -25,18 +25,15 @@ import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.spi.entities.YangResourceEntity import org.onap.cps.spi.exceptions.AlreadyDefinedException import org.onap.cps.spi.exceptions.DataspaceNotFoundException -import org.onap.cps.spi.exceptions.SchemaSetInUseException import org.onap.cps.spi.exceptions.SchemaSetNotFoundException import org.onap.cps.spi.model.ModuleReference import org.onap.cps.spi.model.ExtendedModuleReference import org.onap.cps.spi.repository.AnchorRepository +import org.onap.cps.spi.repository.ModuleReferenceRepository import org.onap.cps.spi.repository.SchemaSetRepository import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.context.jdbc.Sql -import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED -import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED - class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { @Autowired @@ -54,7 +51,6 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase static final String SET_DATA = '/data/schemaset.sql' static final String EXISTING_SCHEMA_SET_NAME = SCHEMA_SET_NAME1 static final String SCHEMA_SET_NAME_NO_ANCHORS = 'SCHEMA-SET-100' - static final String SCHEMA_SET_NAME_WITH_ANCHORS_AND_DATA = 'SCHEMA-SET-101' static final String SCHEMA_SET_NAME_NEW = 'SCHEMA-SET-NEW' static final String NEW_RESOURCE_NAME = 'some new resource' @@ -76,9 +72,7 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase .revision(NEW_RESOURCE_REVISION).build() def newYangResourcesNameToContentMap = [(NEW_RESOURCE_NAME):NEW_RESOURCE_CONTENT] - def allYangResourcesModuleAndRevisionList = [new ExtendedModuleReference(name: 'MODULE-NAME-002',namespace:null, revision: 'REVISION-002'), new ExtendedModuleReference(name: 'MODULE-NAME-003',namespace:null, revision: 'REVISION-003'), - new ExtendedModuleReference(name: 'MODULE-NAME-004',namespace:null, revision: 'REVISION-004'), ExtendedModuleReference.builder().build(), - ExtendedModuleReference.builder().build(), newModuleReference] + def dataspaceEntity def setup() { @@ -184,6 +178,19 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase } @Sql([CLEAR_DATA, SET_DATA]) + def 'Identifying new module references where #scenario'() { + when: 'identifyNewModuleReferences is called' + def result = objectUnderTest.identifyNewModuleReferences(moduleReferences) + then: 'the correct module reference collection is returned' + assert result == expectedResult + where: 'the following data is used' + scenario | moduleReferences || expectedResult + 'new module references exist' | toModuleReference([['some module 1' : 'some revision 1'], ['some module 2' : 'some revision 2']]) || toModuleReference([['some module 1' : 'some revision 1'], ['some module 2' : 'some revision 2']]) + 'no new module references exist' | [] || [] + 'module references collection is null'| null || [] + } + + @Sql([CLEAR_DATA, SET_DATA]) def 'Delete schema set error scenario: #scenario.'() { when: 'attempt to delete a schema set where #scenario' objectUnderTest.deleteSchemaSet(dataspaceName, schemaSetName) @@ -236,4 +243,13 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase yangResourceEntity.revision == expectedYangResourceRevision } + def toModuleReference(moduleReferenceAsMap) { + def moduleReferences = [].withDefault { [:] } + moduleReferenceAsMap.forEach(property -> + property.forEach((moduleName, revision) -> { + moduleReferences.add(new ModuleReference('moduleName' : moduleName, 'revision' : revision)) + })) + return moduleReferences + } + } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy index 8bd7f86ea..9ef973268 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (c) 2021 Bell Canada. + * Modifications 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. @@ -19,9 +20,11 @@ package org.onap.cps.spi.impl import org.hibernate.exception.ConstraintViolationException +import org.onap.cps.spi.CpsAdminPersistenceService import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.spi.exceptions.DuplicatedYangResourceException import org.onap.cps.spi.repository.DataspaceRepository +import org.onap.cps.spi.repository.ModuleReferenceRepository import org.onap.cps.spi.repository.SchemaSetRepository import org.onap.cps.spi.repository.YangResourceRepository import org.springframework.dao.DataIntegrityViolationException @@ -42,6 +45,8 @@ class CpsModulePersistenceServiceSpec extends Specification { def dataspaceRepositoryMock = Mock(DataspaceRepository) def yangResourceRepositoryMock = Mock(YangResourceRepository) def schemaSetRepositoryMock = Mock(SchemaSetRepository) + def cpsAdminPersistenceServiceMock = Mock(CpsAdminPersistenceService) + def moduleReferenceRepositoryMock = Mock(ModuleReferenceRepository) // Constants def yangResourceName = 'my-yang-resource-name' @@ -73,10 +78,8 @@ class CpsModulePersistenceServiceSpec extends Specification { anotherIntegrityException = new DataIntegrityViolationException("another integrity exception") def setup() { - objectUnderTest = new CpsModulePersistenceServiceImpl() - objectUnderTest.dataspaceRepository = dataspaceRepositoryMock - objectUnderTest.yangResourceRepository = yangResourceRepositoryMock - objectUnderTest.schemaSetRepository = schemaSetRepositoryMock + objectUnderTest = new CpsModulePersistenceServiceImpl(yangResourceRepositoryMock, schemaSetRepositoryMock, + dataspaceRepositoryMock, cpsAdminPersistenceServiceMock, moduleReferenceRepositoryMock) } def 'Store schema set error scenario: #scenario.'() { diff --git a/cps-service/pom.xml b/cps-service/pom.xml index bfc3931bc..9c7031e2f 100644 --- a/cps-service/pom.xml +++ b/cps-service/pom.xml @@ -28,7 +28,7 @@ <parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java index 1dccf49c9..6ae28fe9c 100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2020 Nordix Foundation + * Copyright (C) 2020-2022 Nordix Foundation * Modifications Copyright (C) 2020-2021 Pantheon.tech * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,6 @@ package org.onap.cps.api; import java.util.Collection; -import java.util.List; import java.util.Map; import org.checkerframework.checker.nullness.qual.NonNull; import org.onap.cps.spi.CascadeDeleteAllowed; @@ -48,16 +47,14 @@ public interface CpsModuleService { /** * Create a schema set from new modules and existing modules. - * - * @param dataspaceName Dataspace name - * @param schemaSetName schema set name - * @param newYangResourcesModuleNameToContentMap YANG resources map where key is a module name and value is content - * @param moduleReferences List of YANG resources module references of the modules - * needed for this handle that are already in CPS + * @param dataspaceName Dataspace name + * @param schemaSetName schema set name + * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content + * @param moduleReferences List of YANG resources module references of the modules */ void createSchemaSetFromModules(@NonNull String dataspaceName, @NonNull String schemaSetName, - @NonNull Map<String, String> newYangResourcesModuleNameToContentMap, - @NonNull List<ModuleReference> moduleReferences); + @NonNull Map<String, String> newModuleNameToContentMap, + Collection<ModuleReference> moduleReferences); /** * Read schema set in the given dataspace. @@ -96,4 +93,14 @@ public interface CpsModuleService { * @return a list of ModuleReference objects */ Collection<ModuleReference> getYangResourcesModuleReferences(String dataspaceName, String anchorName); + + /** + * Identify previously unknown Yang Resource module references. + * + * @param moduleReferencesToCheck the moduleReferencesToCheck + * @returns collection of module references + */ + Collection<ModuleReference> identifyNewModuleReferences( + Collection<ModuleReference> moduleReferencesToCheck); + } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java index ffcc5a22f..7267f22b5 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2020-2021 Nordix Foundation + * Copyright (C) 2020-2022 Nordix Foundation * Modifications Copyright (C) 2020-2021 Pantheon.tech * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ @@ -23,7 +23,6 @@ package org.onap.cps.api.impl; import java.util.Collection; -import java.util.List; import java.util.Map; import lombok.AllArgsConstructor; import org.onap.cps.api.CpsAdminService; @@ -57,10 +56,10 @@ public class CpsModuleServiceImpl implements CpsModuleService { @Override public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName, - final Map<String, String> newYangResourcesModuleNameToContentMap, - final List<ModuleReference> moduleReferences) { + final Map<String, String> newModuleNameToContentMap, + final Collection<ModuleReference> moduleReferences) { cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName, - newYangResourcesModuleNameToContentMap, moduleReferences); + newModuleNameToContentMap, moduleReferences); } @@ -102,4 +101,11 @@ public class CpsModuleServiceImpl implements CpsModuleService { private boolean isCascadeDeleteProhibited(final CascadeDeleteAllowed cascadeDeleteAllowed) { return CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED == cascadeDeleteAllowed; } + + @Override + public Collection<ModuleReference> identifyNewModuleReferences( + final Collection<ModuleReference> moduleReferencesToCheck) { + return cpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck); + } + } diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java index e08273441..4306df78d 100755 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2020 Nordix Foundation + * Copyright (C) 2020-2022 Nordix Foundation * Modifications Copyright (C) 2020-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,6 @@ package org.onap.cps.spi; import java.util.Collection; -import java.util.List; import java.util.Map; import org.onap.cps.spi.model.ModuleReference; @@ -45,11 +44,11 @@ public interface CpsModulePersistenceService { * * @param dataspaceName Dataspace name * @param schemaSetName Schema set name - * @param newYangResourcesModuleNameToContentMap YANG resources map where key is a module name and value is content + * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content * @param moduleReferences List of YANG resources module references */ void storeSchemaSetFromModules(String dataspaceName, String schemaSetName, - Map<String, String> newYangResourcesModuleNameToContentMap, List<ModuleReference> moduleReferences); + Map<String, String> newModuleNameToContentMap, Collection<ModuleReference> moduleReferences); /** * Deletes Schema Set. @@ -98,4 +97,14 @@ public interface CpsModulePersistenceService { * Remove unused Yang Resource Modules. */ void deleteUnusedYangResourceModules(); + + /** + * Identify new module references from those returned by a node compared to what is in CPS already. + * + * @param moduleReferencesToCheck the module references ot check + * @returns Collection of {@link ModuleReference} of previously unknown module references + */ + Collection<ModuleReference> identifyNewModuleReferences( + Collection<ModuleReference> moduleReferencesToCheck); + } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy index 67dce1daf..afd8e8666 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2020-2021 Nordix Foundation + * Copyright (C) 2020-2022 Nordix Foundation * Modifications Copyright (C) 2020-2021 Pantheon.tech * Modifications Copyright (C) 2020-2022 Bell Canada. * ================================================================================ @@ -37,11 +37,11 @@ import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED class CpsModuleServiceImplSpec extends Specification { - def mockModuleStoreService = Mock(CpsModulePersistenceService) + def mockCpsModulePersistenceService = Mock(CpsModulePersistenceService) def mockCpsAdminService = Mock(CpsAdminService) def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache) - def objectUnderTest = new CpsModuleServiceImpl(mockModuleStoreService, mockYangTextSchemaSourceSetCache, mockCpsAdminService) + def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAdminService) def 'Create schema set.'() { given: 'Valid yang resource as name-to-content map' @@ -49,7 +49,7 @@ class CpsModuleServiceImplSpec extends Specification { when: 'Create schema set method is invoked' objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap) then: 'Parameters are validated and processing is delegated to persistence service' - 1 * mockModuleStoreService.storeSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap) + 1 * mockCpsModulePersistenceService.storeSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap) } def 'Create schema set from new modules and existing modules.'() { @@ -59,8 +59,7 @@ class CpsModuleServiceImplSpec extends Specification { when: 'create schema set from modules method is invoked' objectUnderTest.createSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference) then: 'processing is delegated to persistence service' - 1 * mockModuleStoreService.storeSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference) - + 1 * mockCpsModulePersistenceService.storeSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference) } def 'Create schema set from invalid resources'() { @@ -94,11 +93,11 @@ class CpsModuleServiceImplSpec extends Specification { then: 'anchor deletion is called #numberOfAnchors times' numberOfAnchors * mockCpsAdminService.deleteAnchor('my-dataspace', _) and: 'persistence service method is invoked with same parameters' - 1 * mockModuleStoreService.deleteSchemaSet('my-dataspace', 'my-schemaset') + 1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset') and: 'schema set will be removed from the cache' 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my-dataspace', 'my-schemaset') and: 'orphan yang resources are deleted' - 1 * mockModuleStoreService.deleteUnusedYangResourceModules() + 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules() where: 'following parameters are used' numberOfAnchors << [0, 3] } @@ -111,11 +110,11 @@ class CpsModuleServiceImplSpec extends Specification { then: 'no anchors are deleted' 0 * mockCpsAdminService.deleteAnchor(_, _) and: 'persistence service method is invoked with same parameters' - 1 * mockModuleStoreService.deleteSchemaSet('my-dataspace', 'my-schemaset') + 1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset') and: 'schema set will be removed from the cache' 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my-dataspace', 'my-schemaset') and: 'orphan yang resources are deleted' - 1 * mockModuleStoreService.deleteUnusedYangResourceModules() + 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules() } def 'Delete schema-set when cascade is prohibited and schema-set has anchors.'() { @@ -136,7 +135,7 @@ class CpsModuleServiceImplSpec extends Specification { def 'Get all yang resources module references.'() { given: 'an already present module reference' def moduleReferences = [new ExtendedModuleReference()] - mockModuleStoreService.getYangResourceModuleReferences('someDataspaceName') >> moduleReferences + mockCpsModulePersistenceService.getYangResourceModuleReferences('someDataspaceName') >> moduleReferences expect: 'the list provided by persistence service is returned as result' objectUnderTest.getYangResourceModuleReferences('someDataspaceName') == moduleReferences } @@ -145,8 +144,17 @@ class CpsModuleServiceImplSpec extends Specification { def 'Get all yang resources module references for the given dataspace name and anchor name.'() { given: 'the module store service service returns a list module references' def moduleReferences = [new ModuleReference()] - mockModuleStoreService.getYangResourceModuleReferences('someDataspaceName', 'someAnchorName') >> moduleReferences + mockCpsModulePersistenceService.getYangResourceModuleReferences('someDataspaceName', 'someAnchorName') >> moduleReferences expect: 'the list provided by persistence service is returned as result' objectUnderTest.getYangResourcesModuleReferences('someDataspaceName', 'someAnchorName') == moduleReferences } + + def 'Identifying new module references'(){ + given: 'module references from cm handle' + def moduleReferencesToCheck = [new ModuleReference('some-module', 'some-revision')] + when: 'identifyNewModuleReferences is called' + objectUnderTest.identifyNewModuleReferences(moduleReferencesToCheck) + then: 'cps module persistence service is called with module references to check' + 1 * mockCpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck); + } } diff --git a/csit/tests/cps-model-sync/cps-model-sync.robot b/csit/tests/cps-model-sync/cps-model-sync.robot index 0b9928bef..5021f77e9 100644 --- a/csit/tests/cps-model-sync/cps-model-sync.robot +++ b/csit/tests/cps-model-sync/cps-model-sync.robot @@ -34,14 +34,14 @@ ${auth} Basic Y3BzdXNlcjpjcHNyMGNrcyE= ${ncmpInventoryBasePath} /ncmpInventory ${ncmpBasePath} /ncmp ${dmiUrl} http://${DMI_HOST}:${DMI_PORT} -${jsonData} {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":null,"dmiModelPlugin":null,"createdCmHandles":[{"cmHandle":"PNFDemo","cmHandleProperties":{"Book1":"Sci-Fi Book"},"publicCmHandleProperties":{"Contact":"storeemail@bookstore.com"}}],"updatedCmHandles":[{"cmHandle":"PNFDemo","cmHandleProperties":{"Book1":"Romance Book"},"publicCmHandleProperties":{"Contact":"newemailforstore@bookstore.com"}}]} +${jsonData} {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":"","dmiModelPlugin":"","createdCmHandles":[{"cmHandle":"PNFDemo","cmHandleProperties":{"Book1":"Sci-Fi Book"},"publicCmHandleProperties":{"Contact":"storeemail@bookstore.com"}}],"updatedCmHandles":[{"cmHandle":"PNFDemo","cmHandleProperties":{"Book1":"Romance Book"},"publicCmHandleProperties":{"Contact":"newemailforstore@bookstore.com"}}]} *** Test Cases *** Register node, update data node and sync modules. ${uri}= Set Variable ${ncmpInventoryBasePath}/v1/ch ${headers}= Create Dictionary Content-Type=application/json Authorization=${auth} ${response}= POST On Session CPS_URL ${uri} headers=${headers} data=${jsonData} - Should Be Equal As Strings ${response.status_code} 201 + Should Be Equal As Strings ${response.status_code} 204 Get modules for registered data node ${uri}= Set Variable ${ncmpBasePath}/v1/ch/PNFDemo/modules diff --git a/docs/api/swagger/ncmp/openapi-inventory.yaml b/docs/api/swagger/ncmp/openapi-inventory.yaml index 67eae41c4..34a087b85 100644 --- a/docs/api/swagger/ncmp/openapi-inventory.yaml +++ b/docs/api/swagger/ncmp/openapi-inventory.yaml @@ -20,8 +20,8 @@ paths: $ref: '#/components/schemas/RestDmiPluginRegistration' required: true responses: - "201": - description: Created + "204": + description: No Content content: {} "400": description: Bad Request diff --git a/docs/release-notes.rst b/docs/release-notes.rst index c7d5d39e6..ff8a98867 100755 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -1,6 +1,6 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. .. http://creativecommons.org/licenses/by/4.0 -.. Copyright (C) 2021 Nordix Foundation +.. Copyright (C) 2021-2022 Nordix Foundation .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING .. _release_notes: @@ -19,7 +19,7 @@ CPS Release Notes .. * * * JAKARTA * * * .. ======================== -Version: 2.1.0-SNAPSHOT +Version: 3.0.0-SNAPSHOT ======================= This section lists the main changes & fixes merged into master (snapshot) version of CPS-NCMP. This information is here to assist developers that want experiment/test using our latest code bases directly. Stability of this is not guaranteed. @@ -32,6 +32,7 @@ Features - `CPS-741 <https://jira.onap.org/browse/CPS-741>`_ Re sync after removing cm handles - `CPS-777 <https://jira.onap.org/browse/CPS-777>`_ Ensure all DMI operations use POST method - `CPS-780 <https://jira.onap.org/browse/CPS-780>`_ Add examples for parameters, request and response in openapi yaml for cps-core + - `CPS-837 <https://jira.onap.org/browse/CPS-837>`_ Add Remove and Update properties (DMI and Public) as part of CM Handle Registration update Bug Fixes --------- @@ -40,8 +41,10 @@ Bug Fixes - `CPS-788 <https://jira.onap.org/browse/CPS-788>`_ Yang Resource formatting is incorrect - `CPS-783 <https://jira.onap.org/browse/CPS-783>`_ Remove cm handle does not completely remove all cm handle information - `CPS-841 <https://jira.onap.org/browse/CPS-841>`_ Upgrade log4j to 2.17.1 as recommended by ONAP SECCOM - - `CPS-867 <https://jira.onap.org/browse/CPS-867>`_ Database port made configurable through env variable DB_PORT - `CPS-856 <https://jira.onap.org/browse/CPS-856>`_ Retry mechanism not working for concurrent CmHandle registration + - `CPS-867 <https://jira.onap.org/browse/CPS-867>`_ Database port made configurable through env variable DB_PORT + - `CPS-887 <https://jira.onap.org/browse/CPS-887>`_ Increase performance of cmHandle registration for large number of schema sets in DB + - `CPS-892 <https://jira.onap.org/browse/CPS-892>`_ Fixed the response code during CM-Handle Registration from 201 CREATED to 204 NO_CONTENT Known Limitations, Issues and Workarounds ----------------------------------------- diff --git a/jacoco-report/pom.xml b/jacoco-report/pom.xml index 25a0bfec8..6c2729cc9 100644 --- a/jacoco-report/pom.xml +++ b/jacoco-report/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> @@ -32,7 +32,7 @@ <groupId>org.onap.cps</groupId>
<artifactId>cps-aggregator</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>cps</name>
diff --git a/spotbugs/pom.xml b/spotbugs/pom.xml index 028cd4955..50cef487e 100644 --- a/spotbugs/pom.xml +++ b/spotbugs/pom.xml @@ -25,7 +25,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.onap.cps</groupId> <artifactId>spotbugs</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <properties> <nexusproxy>https://nexus.onap.org</nexusproxy> diff --git a/version.properties b/version.properties index 07ef49adb..17f2daa6c 100755 --- a/version.properties +++ b/version.properties @@ -1,5 +1,6 @@ # ============LICENSE_START======================================================= # Copyright (C) 2021 Nordix Foundation +# 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. @@ -19,8 +20,8 @@ # Note that these variables cannot be structured (e.g. : version.release or version.snapshot etc... ) # because they are used in Jenkins, whose plug-in doesn't support this -major=2 -minor=1 +major=3 +minor=0 patch=0 base_version=${major}.${minor}.${patch} |