From: Toine Siebelink Date: Tue, 17 Oct 2023 13:17:08 +0000 (+0000) Subject: Merge "Add memory usage to integration tests [UPDATED]" X-Git-Tag: 3.3.9~14 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=c29a5fe32db7efb0fdefa3a3d020110aab9731b4;hp=500134c9c745eeda707e5738a5a699c69bb899c6;p=cps.git Merge "Add memory usage to integration tests [UPDATED]" --- diff --git a/.gitignore b/.gitignore index b4abc48bc..c59fc4731 100755 --- a/.gitignore +++ b/.gitignore @@ -27,10 +27,10 @@ tmp/ .checkstyle /.tox -/_build/* /__pycache__/* /docs/docs/ /docs/.vscode/ +/docs/_build/* /test-tools/metrics-reports/ diff --git a/checkstyle/pom.xml b/checkstyle/pom.xml index 4cf950efd..7d190fe10 100644 --- a/checkstyle/pom.xml +++ b/checkstyle/pom.xml @@ -26,7 +26,7 @@ 4.0.0 org.onap.cps checkstyle - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT diff --git a/cps-application/pom.xml b/cps-application/pom.xml index 3b5069a1c..f4e3e916f 100755 --- a/cps-application/pom.xml +++ b/cps-application/pom.xml @@ -28,7 +28,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml @@ -65,6 +65,18 @@ org.springframework.boot spring-boot-starter-actuator + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-http + + + jakarta.servlet + jakarta.servlet-api + io.micrometer micrometer-registry-prometheus @@ -73,6 +85,11 @@ io.micrometer micrometer-tracing-bridge-brave + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + org.springframework.security @@ -99,6 +116,10 @@ cglib-nodep test + + com.tngtech.archunit + archunit-junit5 + org.springframework.boot spring-boot-starter-test @@ -110,26 +131,7 @@ - - com.tngtech.archunit - archunit-junit5 - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - - - org.eclipse.jetty - jetty-server - - - org.eclipse.jetty - jetty-http - - - jakarta.servlet - jakarta.servlet-api - + diff --git a/cps-bom/pom.xml b/cps-bom/pom.xml index 0f5cb4b48..b9da8b29a 100644 --- a/cps-bom/pom.xml +++ b/cps-bom/pom.xml @@ -25,7 +25,7 @@ 4.0.0 org.onap.cps cps-bom - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT pom This artifact contains dependencyManagement declarations of all published CPS components. diff --git a/cps-dependencies/pom.xml b/cps-dependencies/pom.xml index f6931c302..cff596f05 100755 --- a/cps-dependencies/pom.xml +++ b/cps-dependencies/pom.xml @@ -27,7 +27,7 @@ 4.0.0 org.onap.cps cps-dependencies - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT pom ${project.groupId}:${project.artifactId} @@ -41,6 +41,7 @@ true 1.18.3 1.4.2.Final + 11.0.16 @@ -70,6 +71,7 @@ + org.springdoc springdoc-openapi-ui @@ -90,16 +92,9 @@ import - org.opendaylight.yangtools - yangtools-artifacts - 8.0.6 - pom - import - - - io.swagger.core.v3 - swagger-annotations - 2.2.10 + org.springframework + spring-test + 6.0.11 io.springfox @@ -107,94 +102,88 @@ 3.1.2 - com.google.code.gson - gson - 2.8.9 - - - io.hypersistence - hypersistence-utils-hibernate-60 - 3.5.0 + org.springframework.boot + spring-boot-starter-cache + 3.1.2 + - org.antlr - antlr4-runtime - 4.9.2 + cglib + cglib-nodep + 3.1 - org.codehaus.groovy - groovy - ${groovy.version} + com.fasterxml.jackson.core + jackson-databind + 2.15.2 - org.codehaus.groovy - groovy-json - ${groovy.version} + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.13.1 - org.spockframework - spock-core - 2.4-M1-groovy-3.0 + com.github.spotbugs + spotbugs + 4.2.0 - org.spockframework - spock-spring - 2.4-M1-groovy-3.0 + com.google.code.gson + gson + 2.8.9 - cglib - cglib-nodep - 3.1 + com.google.guava + guava + 31.1-jre - io.cloudevents - cloudevents-bom - 2.5.0 - pom - import + com.hazelcast + hazelcast-spring + 5.3.1 - org.apache.commons - commons-lang3 - 3.11 + com.tngtech.archunit + archunit-junit5 + 0.18.0 + test - org.jetbrains - annotations - 22.0.0 + jakarta.validation + jakarta.validation-api + 3.0.2 - org.testcontainers - testcontainers-bom - 1.18.3 + io.cloudevents + cloudevents-bom + 2.5.0 pom import - com.github.spotbugs - spotbugs - 4.2.0 + io.gsonfire + gson-fire + 1.8.5 - org.liquibase - liquibase-core - 4.16.0 + io.hypersistence + hypersistence-utils-hibernate-60 + 3.5.0 - com.tngtech.archunit - archunit-junit5 - 0.18.0 - test + io.micrometer + micrometer-tracing-bridge-brave + 1.0.0 - org.mapstruct - mapstruct - ${mapstruct.version} + io.swagger.core.v3 + swagger-annotations + 2.2.10 - org.mapstruct - mapstruct-processor - ${mapstruct.version} + io.swagger.core.v3 + swagger-models + 2.2.15 net.logstash.logback @@ -202,24 +191,14 @@ 7.0.1 - org.codehaus.janino - janino - 3.1.7 - - - com.hazelcast - hazelcast-spring - 5.3.1 - - - com.google.guava - guava - 31.1-jre + org.antlr + antlr4-runtime + 4.9.2 - javax.validation - validation-api - 2.0.1.Final + org.apache.commons + commons-lang3 + 3.11 org.apache.maven.plugins @@ -227,54 +206,54 @@ 3.3.1 - jakarta.validation - jakarta.validation-api - 3.0.2 + org.codehaus.groovy + groovy + ${groovy.version} - io.micrometer - micrometer-tracing-bridge-brave - 1.0.0 + org.codehaus.janino + janino + 3.1.7 - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - 2.13.1 + org.codehaus.groovy + groovy-json + ${groovy.version} - io.swagger.core.v3 - swagger-models - 2.2.15 + org.eclipse.jetty + jetty-server + ${jetty-version} - org.projectlombok - lombok - 1.18.24 + org.eclipse.jetty + jetty-http + ${jetty-version} - io.gsonfire - gson-fire - 1.8.5 + org.jetbrains + annotations + 22.0.0 - com.fasterxml.jackson.core - jackson-databind - 2.15.2 + org.junit.jupiter + junit-jupiter-api + 5.10.0 - org.eclipse.jetty - jetty-server - 11.0.14 + org.liquibase + liquibase-core + 4.16.0 - org.eclipse.jetty - jetty-http - 11.0.14 + org.mapstruct + mapstruct + ${mapstruct.version} - jakarta.servlet - jakarta.servlet-api - 6.0.0 + org.mapstruct + mapstruct-processor + ${mapstruct.version} org.openapitools @@ -282,14 +261,33 @@ 0.2.4 - org.junit.jupiter - junit-jupiter-api - 5.10.0 + org.opendaylight.yangtools + yangtools-artifacts + 8.0.6 + pom + import - org.springframework - spring-test - 6.0.11 + org.projectlombok + lombok + 1.18.24 + + + org.testcontainers + testcontainers-bom + 1.18.3 + pom + import + + + org.spockframework + spock-core + 2.4-M1-groovy-3.0 + + + org.spockframework + spock-spring + 2.4-M1-groovy-3.0 diff --git a/cps-events/pom.xml b/cps-events/pom.xml index d776ccb09..6d16203a6 100644 --- a/cps-events/pom.xml +++ b/cps-events/pom.xml @@ -24,7 +24,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml diff --git a/cps-ncmp-events/pom.xml b/cps-ncmp-events/pom.xml index 6a5ba4bf5..a59e92510 100644 --- a/cps-ncmp-events/pom.xml +++ b/cps-ncmp-events/pom.xml @@ -23,7 +23,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml diff --git a/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-dmi-in-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-dmi-in-event-schema-1.0.0.json new file mode 100644 index 000000000..caffb6334 --- /dev/null +++ b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-dmi-in-event-schema-1.0.0.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-merge-dmi-in-event-schema:1.0.0", + "$ref": "#/definitions/CmSubscriptionMergeDmiInEvent", + "definitions": { + "CmSubscriptionMergeDmiInEvent": { + "description": "The payload for cm subscription merge event incoming message from NCMP.", + "type": "object", + "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_dmi.CmSubscriptionMergeDmiInEvent", + "additionalProperties": false, + "properties": { + "data": { + "$ref": "#/definitions/data" + } + }, + "required": [ + "data" + ] + }, + "data": { + "type": "object", + "description": "The actual data containing information about the targets and subscription", + "additionalProperties": false, + "properties": { + "cmhandles": { + "type": "array", + "items": { + "type": "object", + "description": "Details for the target cmhandles", + "additionalProperties": false, + "properties": { + "cmhandleId": { + "type": "string" + }, + "private-properties": { + "type": "object", + "existingJavaType": "java.util.Map", + "items": { + "type": "string" + } + } + } + } + }, + "predicates": { + "type": "array", + "description": "Additional values to be added into the subscription", + "items": { + "type": "object", + "properties": { + "targetFilter": { + "description": "CM Handles to be targeted by the subscription", + "type": "array", + "items": { + "type": "string" + } + }, + "scopeFilter": { + "type": "object", + "properties": { + "datastore": { + "description": "datastore which is to be used by the subscription", + "type": "string", + "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"] + }, + "xpath-filter": { + "description": "filter to be applied to the CM Handles through this event", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "xpath-filter" + ] + } + }, + "additionalProperties": false, + "required": [ + "targetFilter" + ] + }, + "additionalProperties": false + } + }, + "required": [ + "cmhandles", + "predicates" + ] + } + } +} \ No newline at end of file diff --git a/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-ncmp-in-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-ncmp-in-event-schema-1.0.0.json new file mode 100644 index 000000000..274206256 --- /dev/null +++ b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-ncmp-in-event-schema-1.0.0.json @@ -0,0 +1,74 @@ +{ + "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-merge-ncmp-in-event:1.0.0", + "$ref": "#/definitions/CmSubscriptionMergeNcmpInEvent", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "definitions": { + "CmSubscriptionMergeNcmpInEvent": { + "description": "The payload for subscription merge event.", + "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.client_to_ncmp.CmSubscriptionMergeNcmpInEvent", + "properties": { + "data": { + "properties": { + "subscriptionId": { + "description": "The subscription details.", + "type": "string" + }, + "predicates": { + "type": "array", + "description": "Additional values to be added into the subscription", + "items": { + "type": "object", + "properties": { + "targetFilter": { + "description": "CM Handles to be targeted by the subscription", + "type": "array", + "items": { + "type": "string" + } + }, + "scopeFilter": { + "type": "object", + "properties": { + "datastore": { + "description": "datastore which is to be used by the subscription", + "type": "string", + "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"] + }, + "xpath-filter": { + "description": "filter to be applied to the CM Handles through this event", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "xpath-filter" + ] + } + }, + "additionalProperties": false, + "required": [ + "targetFilter" + ] + }, + "additionalProperties": false + } + }, + "required": [ + "subscriptionId", + "predicates" + ], + "type": "object", + "additionalProperties": false + } + }, + "type": "object", + "additionalProperties": false, + "required": [ + "data" + ] + } + } +} \ No newline at end of file diff --git a/cps-ncmp-events/src/main/resources/schemas/trustlevel/device-trust-level-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/trustlevel/device-trust-level-event-schema-1.0.0.json new file mode 100644 index 000000000..e1796fbc7 --- /dev/null +++ b/cps-ncmp-events/src/main/resources/schemas/trustlevel/device-trust-level-event-schema-1.0.0.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "urn:cps:org.onap.cps.ncmp.events:device-trust-level-event-schema:1.0.0", + "$ref": "#/definitions/DeviceTrustLevel", + "definitions": { + "DeviceTrustLevel" : { + "description": "The payload for device trust level event.", + "type": "object", + "javaType": "org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel", + "properties": { + "data": { + "type": "object", + "properties": { + "trustLevel": { + "type": "string" + } + }, + "required": [ + "trustLevel" + ], + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + } + } +} \ No newline at end of file diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml index 275313c3b..205a33c89 100644 --- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml +++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml @@ -22,7 +22,7 @@ org.onap.cps cps-ncmp-rest-stub - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT cps-ncmp-rest-stub-app diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml index 435cc7048..d2cce8797 100644 --- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml +++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml @@ -21,7 +21,7 @@ org.onap.cps cps-ncmp-rest-stub - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT cps-ncmp-rest-stub-service @@ -46,11 +46,42 @@ org.onap.cps cps-ncmp-rest + + org.eclipse.jetty + jetty-server + + + jakarta.servlet + jakarta.servlet-api + org.spockframework spock-core test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.spockframework + spock-spring + test + + + org.spockframework + spock-core + test + + \ No newline at end of file diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java index 198b14fc3..e33af45f9 100644 --- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java +++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java @@ -141,13 +141,13 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity createResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier, - final String datastoreName, final String cmHandle, - @Valid final Object body, + public ResponseEntity createResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle, + @NotNull @Valid final String resourceIdentifier, + @Valid final Object body, final String contentType) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } - + @Override public ResponseEntity deleteResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle, @NotNull @Valid final String resourceIdentifier, @@ -183,13 +183,13 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity patchResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier, - final String datastoreName, final String cmHandle, - @Valid final Object body, + public ResponseEntity patchResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle, + @NotNull @Valid final String resourceIdentifier, + @Valid final Object body, final String contentType) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } - + @Override public ResponseEntity queryResourceDataForCmHandle(final String datastoreName, final String cmHandle, @Valid final String cpsPath, @Valid final String options, @@ -218,9 +218,10 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity updateResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier, - final String datastoreName, - final String cmHandle, @Valid final Object body, + public ResponseEntity updateResourceDataRunningForCmHandle(final String datastoreName, + final String cmHandle, + @NotNull @Valid final String resourceIdentifier, + @Valid final Object body, final String contentType) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/SampleCpsNcmpClientSpec.groovy b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/SampleCpsNcmpClientSpec.groovy new file mode 100644 index 000000000..b36e25ada --- /dev/null +++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/SampleCpsNcmpClientSpec.groovy @@ -0,0 +1,82 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.stub + +import org.onap.cps.ncmp.api.impl.operations.DatastoreType +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.test.context.SpringBootContextLoader +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.boot.test.web.server.LocalServerPort +import org.springframework.http.HttpStatus +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.ContextConfiguration + +import com.fasterxml.jackson.core.JsonProcessingException +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.JsonMappingException +import com.fasterxml.jackson.databind.ObjectMapper + +import spock.lang.Shared +import spock.lang.Specification + +@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") +class SampleCpsNcmpClientSpec extends Specification { + + static final String CM_HANDLE = "anything" + + static final String DATA_STORE_NAME = DatastoreType.PASSTHROUGH_OPERATIONAL.getDatastoreName() + + @LocalServerPort + def port + + final def testRestTemplate = new TestRestTemplate() + + @Value('${rest.api.ncmp-stub-base-path}') + def stubBasePath + + @Autowired + ObjectMapper objectMapper + + def 'Test the invocation of the stub API' () throws JsonMappingException, JsonProcessingException { + + when: 'Get resource data for cm handle URL is invoked' + def url = "${getBaseUrl()}/v1/ch/${CM_HANDLE}/data/ds/${DATA_STORE_NAME}?resourceIdentifier=parent&options=(a=1,b=2)" + def response = testRestTemplate.getForEntity(url, String.class) + then: 'Response is OK' + response.getStatusCode() == HttpStatus.OK + + and: 'Response body contains customized stub response that contains correct bookstore category code' + def typeRef = new TypeReference>() {} + def map = objectMapper.readValue(response.getBody(), typeRef) + def obj1 = (Map) map.get("stores:bookstore") + def obj2 = (List) obj1.get("categories") + def obj3 = (Map) obj2.iterator().next() + + assert obj3.get("code") == "02" + } + + def String getBaseUrl() { + return "http://localhost:" + port + stubBasePath + } +} diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/TestApplication.groovy b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/TestApplication.groovy new file mode 100644 index 000000000..79384239a --- /dev/null +++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/TestApplication.groovy @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.stub + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan(basePackages = ["org.onap.cps.ncmp.rest.stub", "org.onap.cps.ncmp.rest.stub.controller", "org.onap.cps.ncmp.rest.api"]) +class TestApplication { +} diff --git a/cps-ncmp-rest-stub/pom.xml b/cps-ncmp-rest-stub/pom.xml index 4db8617b9..9e62ff592 100644 --- a/cps-ncmp-rest-stub/pom.xml +++ b/cps-ncmp-rest-stub/pom.xml @@ -22,7 +22,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml index 9bae794a3..022e2bab7 100644 --- a/cps-ncmp-rest/docs/openapi/components.yaml +++ b/cps-ncmp-rest/docs/openapi/components.yaml @@ -87,6 +87,8 @@ components: items: type: string example: [ my-cm-handle1, my-cm-handle2, my-cm-handle3 ] + upgradedCmHandles: + $ref: '#/components/schemas/UpgradedCmHandles' DmiPluginRegistrationErrorResponse: type: object properties: @@ -102,6 +104,10 @@ components: type: array items: $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse' + failedUpgradeCmHandles: + type: array + items: + $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse' CmHandlerRegistrationErrorResponse: type: object properties: @@ -135,6 +141,20 @@ components: additionalProperties: type: string example: my-property + #Module upgrade schema + UpgradedCmHandles: + required: + - cmHandles + type: object + properties: + cmHandles: + type: array + items: + type: string + example: [ my-cm-handle1, my-cm-handle2, my-cm-handle3 ] + moduleSetTag: + type: string + example: 'my-module-set-tag' #Response Schemas RestModuleReference: diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml index ef34b1d36..6a52d9f1a 100644 --- a/cps-ncmp-rest/pom.xml +++ b/cps-ncmp-rest/pom.xml @@ -27,7 +27,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml @@ -61,6 +61,10 @@ org.springframework.boot spring-boot-starter-jetty + + com.google.code.gson + gson + io.swagger.core.v3 swagger-annotations @@ -77,6 +81,10 @@ org.mapstruct mapstruct-processor + + org.openapitools + jackson-databind-nullable + org.codehaus.groovy @@ -114,17 +122,10 @@ - - com.google.code.gson - gson - - - org.openapitools - jackson-databind-nullable - org.junit.jupiter junit-jupiter + test diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java index b3f36f9c8..af785d565 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java @@ -48,6 +48,9 @@ public interface NcmpRestInputMapper { @Mapping(source = "removedCmHandles", target = "removedCmHandles", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT) + @Mapping(source = "upgradedCmHandles", target = "upgradedCmHandles", + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT) DmiPluginRegistration toDmiPluginRegistration(final RestDmiPluginRegistration restDmiPluginRegistration); @Mapping(source = "cmHandle", target = "cmHandleId") 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 87f9d835a..453abcabb 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 @@ -110,23 +110,23 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor getFailedResponses(dmiPluginRegistrationResponse.getUpdatedCmHandles())); dmiPluginRegistrationErrorResponse.setFailedRemovedCmHandles( getFailedResponses(dmiPluginRegistrationResponse.getRemovedCmHandles())); - + dmiPluginRegistrationErrorResponse.setFailedUpgradeCmHandles( + getFailedResponses(dmiPluginRegistrationResponse.getUpgradedCmHandles())); return dmiPluginRegistrationErrorResponse; } private List getFailedResponses( - final List cmHandleRegistrationResponseList) { + final List cmHandleRegistrationResponseList) { return cmHandleRegistrationResponseList.stream() - .filter(cmHandleRegistrationResponse -> cmHandleRegistrationResponse.getStatus() == Status.FAILURE) - .map(this::toCmHandleRegistrationErrorResponse) - .collect(Collectors.toList()); + .filter(cmHandleRegistrationResponse -> cmHandleRegistrationResponse.getStatus() == Status.FAILURE) + .map(this::toCmHandleRegistrationErrorResponse).collect(Collectors.toList()); } private CmHandlerRegistrationErrorResponse toCmHandleRegistrationErrorResponse( final CmHandleRegistrationResponse registrationResponse) { return new CmHandlerRegistrationErrorResponse() .cmHandle(registrationResponse.getCmHandle()) - .errorCode(registrationResponse.getRegistrationError().errorCode) + .errorCode(registrationResponse.getNcmpResponseStatus().getCode()) .errorText(registrationResponse.getErrorText()); } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java index 82dc4837f..b436540fc 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java @@ -20,6 +20,8 @@ package org.onap.cps.ncmp.rest.mapper; +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.LOCKED_MISBEHAVING; + import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named; @@ -75,8 +77,10 @@ public interface CmHandleStateMapper { @Named("toExternalLockReason") static LockReason toExternalLockReason(CompositeState.LockReason internalLockReason) { final LockReason externalLockReason = new LockReason(); - if (internalLockReason.getLockReasonCategory() != null) { - externalLockReason.setReason("LOCKED_MISBEHAVING"); + if (internalLockReason.getLockReasonCategory() == null) { + externalLockReason.setReason(LOCKED_MISBEHAVING.name()); + } else { + externalLockReason.setReason(internalLockReason.getLockReasonCategory().name()); } externalLockReason.setDetails(internalLockReason.getDetails()); return externalLockReason; diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy index 6bfa5936e..de044d0aa 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy @@ -615,8 +615,7 @@ class NetworkCmProxyControllerSpec extends Specification { def expectedContent = [ '"state":', '"cmHandleState":"ADVISED"', - '"reason":"LOCKED_MISBEHAVING"', - '"details":"lock details"', + '"lockReason":{"reason":"MODULE_SYNC_FAILED","details":"lock details"}', '"lastUpdateTime":"2022-12-31T20:30:40.000+0000"', '"dataSyncEnabled":false', '"dataSyncState":', 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 e755094dd..1d03be107 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 @@ -219,14 +219,14 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { responseBody.getFailedUpdatedCmHandles() == expectedFailedUpdateCmHandle responseBody.getFailedRemovedCmHandles() == expectedFailedRemovedCmHandle where: - scenario | createCmHandleResponse | updateCmHandleResponse | removeCmHandleResponse || expectedFailedCreatedCmHandle | expectedFailedUpdateCmHandle | expectedFailedRemovedCmHandle - 'only create failed' | failedResponse('cm-handle-1') | successResponse('cm-handle-2') | successResponse('cm-handle-3') || [failedRestResponse('cm-handle-1')] | [] | [] - 'only update failed' | successResponse('cm-handle-1') | failedResponse('cm-handle-2') | successResponse('cm-handle-3') || [] | [failedRestResponse('cm-handle-2')] | [] - 'only delete failed' | successResponse('cm-handle-1') | successResponse('cm-handle-2') | failedResponse('cm-handle-3') || [] | [] | [failedRestResponse('cm-handle-3')] - 'all three failed' | failedResponse('cm-handle-1') | failedResponse('cm-handle-2') | failedResponse('cm-handle-3') || [failedRestResponse('cm-handle-1')] | [failedRestResponse('cm-handle-2')] | [failedRestResponse('cm-handle-3')] - 'create update failed' | failedResponse('cm-handle-1') | failedResponse('cm-handle-2') | successResponse('cm-handle-3') || [failedRestResponse('cm-handle-1')] | [failedRestResponse('cm-handle-2')] | [] - 'create delete failed' | failedResponse('cm-handle-1') | successResponse('cm-handle-2') | failedResponse('cm-handle-3') || [failedRestResponse('cm-handle-1')] | [] | [failedRestResponse('cm-handle-3')] - 'update delete failed' | successResponse('cm-handle-1') | failedResponse('cm-handle-2') | failedResponse('cm-handle-3') || [] | [failedRestResponse('cm-handle-2')] | [failedRestResponse('cm-handle-3')] + scenario | createCmHandleResponse | updateCmHandleResponse | removeCmHandleResponse || expectedFailedCreatedCmHandle | expectedFailedUpdateCmHandle | expectedFailedRemovedCmHandle + 'only create failed' | expectedFailedResponse('cm-handle-1') | expectedSuccessResponse('cm-handle-2') | expectedSuccessResponse('cm-handle-3') || [expectedUnknownErrorResponse('cm-handle-1')] | [] | [] + 'only update failed' | expectedSuccessResponse('cm-handle-1') | expectedFailedResponse('cm-handle-2') | expectedSuccessResponse('cm-handle-3') || [] | [expectedUnknownErrorResponse('cm-handle-2')] | [] + 'only delete failed' | expectedSuccessResponse('cm-handle-1') | expectedSuccessResponse('cm-handle-2') | expectedFailedResponse('cm-handle-3') || [] | [] | [expectedUnknownErrorResponse('cm-handle-3')] + 'all three failed' | expectedFailedResponse('cm-handle-1') | expectedFailedResponse('cm-handle-2') | expectedFailedResponse('cm-handle-3') || [expectedUnknownErrorResponse('cm-handle-1')] | [expectedUnknownErrorResponse('cm-handle-2')] | [expectedUnknownErrorResponse('cm-handle-3')] + 'create update failed' | expectedFailedResponse('cm-handle-1') | expectedFailedResponse('cm-handle-2') | expectedSuccessResponse('cm-handle-3') || [expectedUnknownErrorResponse('cm-handle-1')] | [expectedUnknownErrorResponse('cm-handle-2')] | [] + 'create delete failed' | expectedFailedResponse('cm-handle-1') | expectedSuccessResponse('cm-handle-2') | expectedFailedResponse('cm-handle-3') || [expectedUnknownErrorResponse('cm-handle-1')] | [] | [expectedUnknownErrorResponse('cm-handle-3')] + 'update delete failed' | expectedSuccessResponse('cm-handle-1') | expectedFailedResponse('cm-handle-2') | expectedFailedResponse('cm-handle-3') || [] | [expectedUnknownErrorResponse('cm-handle-2')] | [expectedUnknownErrorResponse('cm-handle-3')] } def 'Get all cm handle IDs by DMI plugin identifier.'() { @@ -246,15 +246,15 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { assert response.contentAsString.contains('cm-handle-id-2') } - def failedRestResponse(cmHandle) { - return new CmHandlerRegistrationErrorResponse('cmHandle': cmHandle, 'errorCode': '00', 'errorText': 'Failed') + def expectedUnknownErrorResponse(cmHandle) { + return new CmHandlerRegistrationErrorResponse('cmHandle': cmHandle, 'errorCode': '108', 'errorText': 'Failed') } - def failedResponse(cmHandle) { + def expectedFailedResponse(cmHandle) { return CmHandleRegistrationResponse.createFailureResponse(cmHandle, new RuntimeException('Failed')) } - def successResponse(cmHandle) { + def expectedSuccessResponse(cmHandle) { return CmHandleRegistrationResponse.createSuccessResponse(cmHandle) } diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy index 1fa83a539..f394f9193 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package org.onap.cps.ncmp.rest.mapper +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.LOCKED_MISBEHAVING import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_SYNC_FAILED import org.mapstruct.factory.Mappers @@ -55,7 +56,7 @@ class CmHandleStateMapperSpec extends Specification { and: 'mapped result should have correct values' assert !result.dataSyncEnabled assert result.lastUpdateTime == formattedDateAndTime - assert result.lockReason.reason == 'LOCKED_MISBEHAVING' + assert result.lockReason.reason == MODULE_SYNC_FAILED.name() assert result.lockReason.details == 'locked details' assert result.cmHandleState == 'ADVISED' assert result.dataSyncState.operational.getSyncState() != null @@ -69,17 +70,17 @@ class CmHandleStateMapperSpec extends Specification { def 'Internal to External Lock Reason Mapping of #scenario'() { given: 'a LOCKED composite state with locked reason of #scenario' - def compositeState = new CompositeStateBuilder() + def compositeState = new CompositeStateBuilder() .withCmHandleState(CmHandleState.LOCKED) .withLockReason(lockReason, '').build() when: 'the composite state is mapped to a CMHandle composite state' - def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState) + def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState) then: 'the composite state contains the expected lock Reason and details' - result.getLockReason().getReason() == expectedExternalLockReason + result.getLockReason().getReason() == (expectedExternalLockReason as String) where: scenario | lockReason || expectedExternalLockReason - 'MODULE_SYNC_FAILED' | MODULE_SYNC_FAILED || 'LOCKED_MISBEHAVING' - 'null value' | null || null + 'MODULE_SYNC_FAILED' | MODULE_SYNC_FAILED || MODULE_SYNC_FAILED + 'null value' | null || LOCKED_MISBEHAVING } } diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index 527d70b55..d7c177473 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -27,14 +27,14 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml cps-ncmp-service - 0.98 + 0.96 diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpEventResponseCode.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java similarity index 79% rename from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpEventResponseCode.java rename to cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java index 3b1124983..b9c834c55 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpEventResponseCode.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java @@ -23,7 +23,7 @@ package org.onap.cps.ncmp.api; import lombok.Getter; @Getter -public enum NcmpEventResponseCode { +public enum NcmpResponseStatus { SUCCESS("0", "Successfully applied changes"), SUCCESSFULLY_APPLIED_SUBSCRIPTION("1", "successfully applied subscription"), @@ -33,13 +33,16 @@ public enum NcmpEventResponseCode { UNABLE_TO_READ_RESOURCE_DATA("103", "dmi plugin service is not able to read resource data"), PARTIALLY_APPLIED_SUBSCRIPTION("104", "partially applied subscription"), SUBSCRIPTION_NOT_APPLICABLE("105", "subscription not applicable for all cm handles"), - SUBSCRIPTION_PENDING("106", "subscription pending for all cm handles"); + SUBSCRIPTION_PENDING("106", "subscription pending for all cm handles"), + UNKNOWN_ERROR("108", "Unknown error"), + CM_HANDLE_ALREADY_EXIST("109", "cm-handle already exists"), + CM_HANDLE_INVALID_ID("110", "cm-handle has an invalid character(s) in id"); - private final String statusCode; - private final String statusMessage; + private final String code; + private final String message; - NcmpEventResponseCode(final String statusCode, final String statusMessage) { - this.statusCode = statusCode; - this.statusMessage = statusMessage; + NcmpResponseStatus(final String code, final String message) { + this.code = code; + this.message = message; } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java index 58732b207..1f6c94869 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java @@ -24,6 +24,7 @@ import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DM import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_MODULES; import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_PROPERTIES; import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_CPS_PATH; +import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_TRUST_LEVEL; import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCpsPathConditionProperties; import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateModuleNameConditionProperties; import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle; @@ -70,7 +71,8 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH return executeQueries(cmHandleQueryServiceParameters, this::executeCpsPathQuery, this::queryCmHandlesByPublicProperties, - this::executeModuleNameQuery); + this::executeModuleNameQuery, + this::queryCmHandlesByTrustLevel); } @Override @@ -117,9 +119,10 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), InventoryQueryConditions.HAS_ALL_ADDITIONAL_PROPERTIES.getName()); - return privatePropertyQueryPairs.isEmpty() - ? NO_QUERY_TO_EXECUTE - : cmHandleQueries.queryCmHandleAdditionalProperties(privatePropertyQueryPairs); + if (privatePropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return cmHandleQueries.queryCmHandleAdditionalProperties(privatePropertyQueryPairs); } private Collection queryCmHandlesByPublicProperties( @@ -129,9 +132,23 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), HAS_ALL_PROPERTIES.getConditionName()); - return publicPropertyQueryPairs.isEmpty() - ? NO_QUERY_TO_EXECUTE - : cmHandleQueries.queryCmHandlePublicProperties(publicPropertyQueryPairs); + if (publicPropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return cmHandleQueries.queryCmHandlePublicProperties(publicPropertyQueryPairs); + } + + private Collection queryCmHandlesByTrustLevel(final CmHandleQueryServiceParameters + cmHandleQueryServiceParameters) { + + final Map trustLevelPropertyQueryPairs = + getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), + WITH_TRUST_LEVEL.getConditionName()); + + if (trustLevelPropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return cmHandleQueries.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs); } private Collection executeModuleNameQuery( @@ -157,7 +174,8 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH } try { cpsPathQueryResult = collectCmHandleIdsFromDataNodes( - cmHandleQueries.queryCmHandleDataNodesByCpsPath(cpsPathCondition.get("cpsPath"), OMIT_DESCENDANTS)); + cmHandleQueries.queryCmHandleAncestorsByCpsPath( + cpsPathCondition.get("cpsPath"), OMIT_DESCENDANTS)); } catch (final PathParsingException pathParsingException) { throw new DataValidationException(pathParsingException.getMessage(), pathParsingException.getDetails(), pathParsingException); 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 f8adde85f..a37b27199 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 @@ -24,12 +24,18 @@ package org.onap.cps.ncmp.api.impl; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE; import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCmHandleQueryParameters; import com.google.common.collect.Lists; import com.hazelcast.map.IMap; +import java.text.MessageFormat; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; @@ -48,6 +54,7 @@ import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; import org.onap.cps.ncmp.api.impl.inventory.CompositeState; +import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder; import org.onap.cps.ncmp.api.impl.inventory.CompositeStateUtils; import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState; import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; @@ -61,11 +68,11 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; -import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError; import org.onap.cps.ncmp.api.models.DataOperationRequest; import org.onap.cps.ncmp.api.models.DmiPluginRegistration; import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.models.UpgradedCmHandles; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.CpsException; @@ -92,7 +99,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler; private final CpsDataService cpsDataService; private final IMap moduleSyncStartedOnCmHandles; - private final IMap trustLevelPerDmiPlugin; + private final Map trustLevelPerDmiPlugin; @Override public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( @@ -102,18 +109,23 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService if (!dmiPluginRegistration.getRemovedCmHandles().isEmpty()) { dmiPluginRegistrationResponse.setRemovedCmHandles( - parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration.getRemovedCmHandles())); + parseAndProcessDeletedCmHandlesInRegistration(dmiPluginRegistration.getRemovedCmHandles())); } if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) { dmiPluginRegistrationResponse.setCreatedCmHandles( - parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)); + parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration)); } if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) { dmiPluginRegistrationResponse.setUpdatedCmHandles( networkCmProxyDataServicePropertyHandler .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles())); } + if (dmiPluginRegistration.getUpgradedCmHandles() != null + && !dmiPluginRegistration.getUpgradedCmHandles().getCmHandles().isEmpty()) { + dmiPluginRegistrationResponse.setUpgradedCmHandles( + parseAndProcessUpgradedCmHandlesInRegistration(dmiPluginRegistration)); + } setTrustLevelPerDmiPlugin(dmiPluginRegistration); @@ -210,8 +222,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService */ @Override public void setDataSyncEnabled(final String cmHandleId, final boolean dataSyncEnabled) { - final CompositeState compositeState = inventoryPersistence - .getCmHandleState(cmHandleId); + final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); if (compositeState.getDataSyncEnabled().equals(dataSyncEnabled)) { log.info("Data-Sync Enabled flag is already: {} ", dataSyncEnabled); } else if (compositeState.getCmHandleState() != CmHandleState.READY) { @@ -274,8 +285,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService */ @Override public Map getCmHandlePublicProperties(final String cmHandleId) { - final YangModelCmHandle yangModelCmHandle = - inventoryPersistence.getYangModelCmHandle(cmHandleId); + final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); final List yangModelPublicProperties = yangModelCmHandle.getPublicProperties(); final Map cmHandlePublicProperties = new HashMap<>(); YangDataConverter.asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties); @@ -299,7 +309,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService * @param dmiPluginRegistration dmi plugin registration information. * @return cm-handle registration response for create cm-handle requests. */ - public List parseAndCreateCmHandlesInDmiRegistrationAndSyncModules( + public List parseAndProcessCreatedCmHandlesInRegistration( final DmiPluginRegistration dmiPluginRegistration) { final Map cmHandleStatePerCmHandle = new HashMap<>(); dmiPluginRegistration.getCreatedCmHandles() @@ -308,18 +318,19 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService dmiPluginRegistration.getDmiPlugin(), dmiPluginRegistration.getDmiDataPlugin(), dmiPluginRegistration.getDmiModelPlugin(), - cmHandle); + cmHandle, + cmHandle.getModuleSetTag()); cmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.ADVISED); }); return registerNewCmHandles(cmHandleStatePerCmHandle); } - protected List parseAndRemoveCmHandlesInDmiRegistration( + protected List parseAndProcessDeletedCmHandlesInRegistration( final List tobeRemovedCmHandles) { final List cmHandleRegistrationResponses = new ArrayList<>(tobeRemovedCmHandles.size()); final Collection yangModelCmHandles = - inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles); + inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles); updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING); @@ -349,6 +360,42 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return cmHandleRegistrationResponses; } + protected List parseAndProcessUpgradedCmHandlesInRegistration( + final DmiPluginRegistration dmiPluginRegistration) { + + final UpgradedCmHandles upgradedCmHandles = dmiPluginRegistration.getUpgradedCmHandles(); + final String moduleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag(); + final Map cmHandleStatePerCmHandle = + new HashMap<>(upgradedCmHandles.getCmHandles().size()); + final Collection notReadyCmHandles = new ArrayList<>(upgradedCmHandles.getCmHandles().size()); + final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle(); + final String formattedModuleSetTag = MessageFormat.format("new moduleSetTag: {0}", moduleSetTag); + + upgradedCmHandles.getCmHandles().forEach(cmHandleId -> { + if (cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY)) { + ncmpServiceCmHandle.setCmHandleId(cmHandleId); + ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder() + .withCmHandleState(CmHandleState.READY) + .withLockReason(MODULE_UPGRADE, formattedModuleSetTag).build()); + final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle( + dmiPluginRegistration.getDmiPlugin(), + dmiPluginRegistration.getDmiDataPlugin(), + dmiPluginRegistration.getDmiModelPlugin(), + ncmpServiceCmHandle, + moduleSetTag); + cmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED); + } else { + notReadyCmHandles.add(cmHandleId); + } + }); + + final List cmHandleRegistrationResponses + = upgradeCmHandles(cmHandleStatePerCmHandle); + cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses(notReadyCmHandles, + CM_HANDLES_NOT_READY)); + return cmHandleRegistrationResponses; + } + private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) { try { deleteCmHandleFromDbAndModuleSyncMap(cmHandleId); @@ -356,13 +403,11 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } catch (final DataNodeNotFoundException dataNodeNotFoundException) { log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", cmHandleId, dataNodeNotFoundException.getMessage()); - return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, - RegistrationError.CM_HANDLE_DOES_NOT_EXIST); + return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND); } catch (final DataValidationException dataValidationException) { log.error("Unable to de-register cm-handle id: {}, caused by: {}", cmHandleId, dataValidationException.getMessage()); - return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, - RegistrationError.CM_HANDLE_INVALID_ID); + return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID); } catch (final Exception exception) { log.error("Unable to de-register cm-handle id : {} , caused by : {}", cmHandleId, exception.getMessage()); return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception); @@ -404,20 +449,35 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private List registerNewCmHandles(final Map cmHandleStatePerCmHandle) { - final List cmHandleIds = cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId) - .collect(Collectors.toList()); + final List cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle); try { lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds); } catch (final AlreadyDefinedException alreadyDefinedException) { return CmHandleRegistrationResponse.createFailureResponses( alreadyDefinedException.getAlreadyDefinedObjectNames(), - RegistrationError.CM_HANDLE_ALREADY_EXIST); + CM_HANDLE_ALREADY_EXIST); + } catch (final Exception exception) { + return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, exception); + } + } + + private List upgradeCmHandles(final Map + cmHandleStatePerCmHandle) { + final List cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle); + log.info("Moving cm handles : {} into locked (for upgrade) state.", cmHandleIds); + try { + lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); + return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds); } catch (final Exception exception) { return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, exception); } } + private static List getCmHandleIds(final Map cmHandleStatePerCmHandle) { + return cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId).toList(); + } + private void setTrustLevelPerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) { if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) { trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiPlugin(), TrustLevel.COMPLETE); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java index 2f61b228b..401b19036 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java @@ -22,6 +22,8 @@ package org.onap.cps.ncmp.api.impl; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY; import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.PUBLIC_PROPERTY; @@ -38,7 +40,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; -import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; @@ -75,13 +76,12 @@ public class NetworkCmProxyDataServicePropertyHandler { log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", cmHandleId, e.getMessage()); cmHandleRegistrationResponses.add(CmHandleRegistrationResponse - .createFailureResponse(cmHandleId, RegistrationError.CM_HANDLE_DOES_NOT_EXIST)); + .createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND)); } catch (final DataValidationException e) { log.error("Unable to update cm handle : {}, caused by : {}", cmHandleId, e.getMessage()); cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, - RegistrationError.CM_HANDLE_INVALID_ID)); + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID)); } catch (final Exception exception) { log.error("Unable to update cmHandle : {} , caused by : {}", cmHandleId, exception.getMessage()); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java index 6a8310c20..e8ce050b2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java @@ -75,10 +75,8 @@ public class DmiRestClient { final HttpEntity httpHeaders = new HttpEntity<>(configureHttpHeaders(new HttpHeaders())); final JsonNode dmiPluginHealthStatus = restTemplate.getForObject(dmiPluginBaseUrl + "/manage/health", JsonNode.class, httpHeaders); - if (dmiPluginHealthStatus != null) { - if (dmiPluginHealthStatus.get("status").asText().equals("UP")) { - return DmiPluginStatus.UP; - } + if (dmiPluginHealthStatus != null && dmiPluginHealthStatus.get("status").asText().equals("UP")) { + return DmiPluginStatus.UP; } } catch (final Exception exception) { log.warn("Could not send request for health check since {}", exception.getMessage()); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java index ebe99057d..171db5299 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java @@ -20,10 +20,8 @@ package org.onap.cps.ncmp.api.impl.config.embeddedcache; -import com.hazelcast.collection.ISet; import com.hazelcast.config.MapConfig; -import com.hazelcast.config.SetConfig; -import com.hazelcast.map.IMap; +import java.util.Map; import org.onap.cps.cache.HazelcastCacheConfig; import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; import org.springframework.context.annotation.Bean; @@ -32,21 +30,21 @@ import org.springframework.context.annotation.Configuration; @Configuration public class TrustLevelCacheConfig extends HazelcastCacheConfig { - private static final SetConfig untrustworthyCmHandlesSetConfig = - createSetConfig("untrustworthyCmHandlesSetConfig"); + private static final MapConfig trustLevelPerCmHandleCacheConfig = + createMapConfig("trustLevelPerCmHandleCacheConfig"); private static final MapConfig trustLevelPerDmiPluginCacheConfig = createMapConfig("trustLevelPerDmiPluginCacheConfig"); /** - * Distributed collection of untrustworthy cm handles. + * Distributed instance of trust level cache containing the trust level per cm handle. * - * @return instance of distributed set of untrustworthy cm handles. + * @return configured map of cm handle name as keys to trust-level for values. */ @Bean - public ISet untrustworthyCmHandlesSet() { - return createHazelcastInstance("untrustworthyCmHandlesSet", - untrustworthyCmHandlesSetConfig).getSet("untrustworthyCmHandlesSet"); + public Map trustLevelPerCmHandle() { + return createHazelcastInstance("hazelcastInstanceTrustLevelPerCmHandleMap", + trustLevelPerCmHandleCacheConfig).getMap("trustLevelPerCmHandle"); } /** @@ -55,7 +53,7 @@ public class TrustLevelCacheConfig extends HazelcastCacheConfig { * @return configured map of dmi-plugin name as keys to trust-level for values. */ @Bean - public IMap trustLevelPerDmiPlugin() { + public Map trustLevelPerDmiPlugin() { return createHazelcastInstance("hazelcastInstanceTrustLevelPerDmiPluginMap", trustLevelPerDmiPluginCacheConfig).getMap("trustLevelPerDmiPlugin"); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java index 49e455e58..355e5cdf7 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java @@ -64,8 +64,8 @@ public class EventsPublisher { cloudEventKafkaTemplate.send(topicName, eventKey, event); eventFuture.whenComplete((result, e) -> { if (e == null) { - log.debug("Successfully published event to topic : {} , Event : {}", - result.getRecordMetadata().topic(), result.getProducerRecord().value()); + log.debug("Successfully published event to topic : {} , Event : {}", result.getRecordMetadata().topic(), + result.getProducerRecord().value()); } else { log.error("Unable to publish event to topic : {} due to {}", topicName, e.getMessage()); @@ -85,14 +85,7 @@ public class EventsPublisher { public void publishEvent(final String topicName, final String eventKey, final T event) { final CompletableFuture> eventFuture = legacyKafkaEventTemplate.send(topicName, eventKey, event); - eventFuture.whenComplete((result, e) -> { - if (e == null) { - log.debug("Successfully published event to topic : {} , Event : {}", - result.getRecordMetadata().topic(), result.getProducerRecord().value()); - } else { - log.error("Unable to publish event to topic : {} due to {}", topicName, e.getMessage()); - } - }); + handleLegacyEventCallback(topicName, eventFuture); } /** @@ -107,16 +100,8 @@ public class EventsPublisher { final ProducerRecord producerRecord = new ProducerRecord<>(topicName, null, eventKey, event, eventHeaders); - final CompletableFuture> eventFuture = - legacyKafkaEventTemplate.send(producerRecord); - eventFuture.whenComplete((result, ex) -> { - if (ex != null) { - log.error("Unable to publish event to topic : {} due to {}", topicName, ex.getMessage()); - } else { - log.debug("Successfully published event to topic : {} , Event : {}", - result.getRecordMetadata().topic(), result.getProducerRecord().value()); - } - }); + final CompletableFuture> eventFuture = legacyKafkaEventTemplate.send(producerRecord); + handleLegacyEventCallback(topicName, eventFuture); } /** @@ -133,6 +118,18 @@ public class EventsPublisher { publishEvent(topicName, eventKey, convertToKafkaHeaders(eventHeaders), event); } + private void handleLegacyEventCallback(final String topicName, + final CompletableFuture> eventFuture) { + eventFuture.whenComplete((result, e) -> { + if (e != null) { + log.error("Unable to publish event to topic : {} due to {}", topicName, e.getMessage()); + } else { + log.debug("Successfully published event to topic : {} , Event : {}", result.getRecordMetadata().topic(), + result.getProducerRecord().value()); + } + }); + } + private Headers convertToKafkaHeaders(final Map eventMessageHeaders) { final Headers eventHeaders = new RecordHeaders(); eventMessageHeaders.forEach((key, value) -> eventHeaders.add(key, SerializationUtils.serialize(value))); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisher.java index 473538c93..a0fd81c12 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisher.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisher.java @@ -20,13 +20,20 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.PARTIALLY_APPLIED_SUBSCRIPTION; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_NOT_APPLICABLE; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_PENDING; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESSFULLY_APPLIED_SUBSCRIPTION; +import static org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.ACCEPTED; +import static org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.PENDING; +import static org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.REJECTED; + import io.cloudevents.CloudEvent; 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.ncmp.api.NcmpEventResponseCode; +import org.onap.cps.ncmp.api.NcmpResponseStatus; import org.onap.cps.ncmp.api.impl.events.EventsPublisher; import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence; import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus; @@ -100,48 +107,39 @@ public class CmSubscriptionNcmpOutEventPublisher { cmSubscriptionStatus.setStatus(SubscriptionStatus.fromString(status)); cmSubscriptionStatus.setDetails(details); return cmSubscriptionStatus; - }).collect(Collectors.toList()); + }).toList(); } - private NcmpEventResponseCode decideOnNcmpEventResponseCodeForSubscription( + private NcmpResponseStatus decideOnNcmpEventResponseCodeForSubscription( final Map> cmHandleIdToStatusAndDetailsAsMap) { - final boolean isAllTargetsPending = isAllTargetCmHandleStatusMatch(cmHandleIdToStatusAndDetailsAsMap, - SubscriptionStatus.PENDING); - - final boolean isAllTargetsRejected = isAllTargetCmHandleStatusMatch(cmHandleIdToStatusAndDetailsAsMap, - SubscriptionStatus.REJECTED); - - final boolean isAllTargetsAccepted = isAllTargetCmHandleStatusMatch(cmHandleIdToStatusAndDetailsAsMap, - SubscriptionStatus.ACCEPTED); - - if (isAllTargetsAccepted) { - return NcmpEventResponseCode.SUCCESSFULLY_APPLIED_SUBSCRIPTION; - } else if (isAllTargetsRejected) { - return NcmpEventResponseCode.SUBSCRIPTION_NOT_APPLICABLE; - } else if (isAllTargetsPending) { - return NcmpEventResponseCode.SUBSCRIPTION_PENDING; - } else { - return NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION; + if (allTargetsHaveStatus(cmHandleIdToStatusAndDetailsAsMap, ACCEPTED)) { + return SUCCESSFULLY_APPLIED_SUBSCRIPTION; + } + if (allTargetsHaveStatus(cmHandleIdToStatusAndDetailsAsMap, REJECTED)) { + return SUBSCRIPTION_NOT_APPLICABLE; + } + if (allTargetsHaveStatus(cmHandleIdToStatusAndDetailsAsMap, PENDING)) { + return SUBSCRIPTION_PENDING; } + return PARTIALLY_APPLIED_SUBSCRIPTION; } - private boolean isAllTargetCmHandleStatusMatch( - final Map> cmHandleIdToStatusAndDetailsAsMap, - final SubscriptionStatus subscriptionStatus) { + private boolean allTargetsHaveStatus(final Map> cmHandleIdToStatusAndDetailsAsMap, + final SubscriptionStatus subscriptionStatus) { return cmHandleIdToStatusAndDetailsAsMap.values().stream() .allMatch(entryset -> entryset.containsValue(subscriptionStatus.toString())); } private CmSubscriptionNcmpOutEvent fromCmSubscriptionEvent( final CmSubscriptionEvent cmSubscriptionEvent, - final NcmpEventResponseCode ncmpEventResponseCode) { + final NcmpResponseStatus ncmpResponseStatus) { final CmSubscriptionNcmpOutEvent cmSubscriptionNcmpOutEvent = cmSubscriptionEventToCmSubscriptionNcmpOutEventMapper.toCmSubscriptionNcmpOutEvent( cmSubscriptionEvent); - cmSubscriptionNcmpOutEvent.getData().setStatusCode(Integer.parseInt(ncmpEventResponseCode.getStatusCode())); - cmSubscriptionNcmpOutEvent.getData().setStatusMessage(ncmpEventResponseCode.getStatusMessage()); + cmSubscriptionNcmpOutEvent.getData().setStatusCode(Integer.parseInt(ncmpResponseStatus.getCode())); + cmSubscriptionNcmpOutEvent.getData().setStatusMessage(ncmpResponseStatus.getMessage()); return cmSubscriptionNcmpOutEvent; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java index 98ba95386..4120970e5 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java @@ -25,7 +25,6 @@ import io.cloudevents.CloudEvent; import io.cloudevents.core.CloudEventUtils; import io.cloudevents.core.data.PojoCloudEventData; import io.cloudevents.jackson.PojoCloudEventDataMapper; -import io.cloudevents.rw.CloudEventRWException; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -51,9 +50,9 @@ public class CloudEventMapper { mappedCloudEvent = CloudEventUtils.mapData(cloudEvent, PojoCloudEventDataMapper.from(objectMapper, targetEventClass)); - } catch (final CloudEventRWException cloudEventRwException) { + } catch (final RuntimeException runtimeException) { log.error("Unable to map cloud event to target event class type : {} with cause : {}", targetEventClass, - cloudEventRwException.getMessage()); + runtimeException.getMessage()); } return mappedCloudEvent == null ? null : mappedCloudEvent.getValue(); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java index 4776788c8..81467dbb3 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java @@ -44,6 +44,14 @@ public interface CmHandleQueries { */ Collection queryCmHandlePublicProperties(Map publicPropertyQueryPairs); + /** + * Query CmHandles based on Trust Level. + * + * @param trustLevelPropertyQueryPairs trust level properties for query + * @return CmHandles which have desired trust level + */ + Collection queryCmHandlesByTrustLevel(Map trustLevelPropertyQueryPairs); + /** * Method which returns cm handles by the cm handles state. * @@ -52,13 +60,22 @@ public interface CmHandleQueries { */ List queryCmHandlesByState(CmHandleState cmHandleState); + /** + * Method to return data nodes with ancestor representing the cm handles. + * + * @param cpsPath cps path for which the cmHandle is requested + * @return a list of data nodes representing the cm handles. + */ + List queryCmHandleAncestorsByCpsPath(String cpsPath, + FetchDescendantsOption fetchDescendantsOption); + /** * Method to return data nodes representing the cm handles. * * @param cpsPath cps path for which the cmHandle is requested * @return a list of data nodes representing the cm handles. */ - List queryCmHandleDataNodesByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption); + List queryNcmpRegistryByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption); /** * Method to check the state of a cm handle with given id. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java index b3ade4f1c..e5cf8edd6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java @@ -35,6 +35,8 @@ import java.util.Map; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType; +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelFilter; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; @@ -45,9 +47,9 @@ import org.springframework.stereotype.Component; public class CmHandleQueriesImpl implements CmHandleQueries { private static final String DESCENDANT_PATH = "//"; - - private final CpsDataPersistenceService cpsDataPersistenceService; private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles"; + private final CpsDataPersistenceService cpsDataPersistenceService; + private final Map trustLevelPerCmHandle; @Override public Collection queryCmHandleAdditionalProperties(final Map privatePropertyQueryPairs) { @@ -59,17 +61,32 @@ public class CmHandleQueriesImpl implements CmHandleQueries { return queryCmHandleAnyProperties(publicPropertyQueryPairs, PropertyType.PUBLIC); } + @Override + public Collection queryCmHandlesByTrustLevel(final Map trustLevelPropertyQueryPairs) { + final String trustLevelProperty = trustLevelPropertyQueryPairs.values().iterator().next(); + final TrustLevel targetTrustLevel = TrustLevel.valueOf(trustLevelProperty); + + final TrustLevelFilter trustLevelFilter = new TrustLevelFilter(targetTrustLevel, trustLevelPerCmHandle); + return trustLevelFilter.getAllCmHandleIdsByTargetTrustLevel(); + } + @Override public List queryCmHandlesByState(final CmHandleState cmHandleState) { - return queryCmHandleDataNodesByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]", + return queryCmHandleAncestorsByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]", INCLUDE_ALL_DESCENDANTS); } @Override - public List queryCmHandleDataNodesByCpsPath(final String cpsPath, - final FetchDescendantsOption fetchDescendantsOption) { + public List queryNcmpRegistryByCpsPath(final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption) { return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption); + cpsPath, fetchDescendantsOption); + } + + @Override + public List queryCmHandleAncestorsByCpsPath(final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption) { + return queryNcmpRegistryByCpsPath(cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption); } @Override @@ -81,7 +98,7 @@ public class CmHandleQueriesImpl implements CmHandleQueries { @Override public List queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) { - return queryCmHandleDataNodesByCpsPath("//state/datastores" + "/operational[@sync-state=\"" + return queryCmHandleAncestorsByCpsPath("//state/datastores" + "/operational[@sync-state=\"" + dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS); } @@ -114,7 +131,8 @@ public class CmHandleQueriesImpl implements CmHandleQueries { + publicPropertyQueryPair.getKey() + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]"; - final Collection dataNodes = queryCmHandleDataNodesByCpsPath(cpsPath, OMIT_DESCENDANTS); + final Collection dataNodes = queryCmHandleAncestorsByCpsPath(cpsPath, + OMIT_DESCENDANTS); if (cmHandleIds == null) { cmHandleIds = collectCmHandleIdsFromDataNodes(dataNodes); } else { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java index ef4b299e1..99cca8c0b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java @@ -100,7 +100,9 @@ public class CompositeStateUtils { compositeState.setLastUpdateTimeNow(); final String oldLockReasonDetails = compositeState.getLockReason().getDetails(); final CompositeState.LockReason lockReason = - CompositeState.LockReason.builder().details(oldLockReasonDetails).build(); + CompositeState.LockReason.builder() + .lockReasonCategory(compositeState.getLockReason().getLockReasonCategory()) + .details(oldLockReasonDetails).build(); compositeState.setLockReason(lockReason); }; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java index 8306619f2..e2b2c6b4a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java @@ -21,5 +21,8 @@ package org.onap.cps.ncmp.api.impl.inventory; public enum LockReasonCategory { - MODULE_SYNC_FAILED, MODULE_UPGRADE, MODULE_UPGRADE_FAILED + MODULE_SYNC_FAILED, + MODULE_UPGRADE, + MODULE_UPGRADE_FAILED, + LOCKED_MISBEHAVING } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java index b2949c278..8e17ab916 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java @@ -20,20 +20,38 @@ package org.onap.cps.ncmp.api.impl.inventory.sync; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.onap.cps.api.CpsAdminService; +import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsModuleService; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; +import org.onap.cps.ncmp.api.impl.inventory.CompositeState; +import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory; import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations; +import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.spi.CascadeDeleteAllowed; +import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.SchemaSetNotFoundException; +import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleReference; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Service; @Slf4j @@ -43,16 +61,31 @@ public class ModuleSyncService { private final DmiModelOperations dmiModelOperations; private final CpsModuleService cpsModuleService; - private final CpsAdminService cpsAdminService; + private final CmHandleQueries cmHandleQueries; + private final CpsDataService cpsDataService; + private final JsonObjectMapper jsonObjectMapper; /** * This method registers a cm handle and initiates modules sync. * - * @param yangModelCmHandle the yang model of cm handle. + * @param upgradedCmHandle the yang model of cm handle. */ - public void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) { + public void syncAndCreateOrUpgradeSchemaSetAndAnchor(final YangModelCmHandle upgradedCmHandle) { + + final String moduleSetTag = extractModuleSetTag(upgradedCmHandle.getCompositeState()); + final Optional existingCmHandleWithSameModuleSetTag + = getFirstReadyDataNodeWithModuleSetTag(moduleSetTag); + if (existingCmHandleWithSameModuleSetTag.isPresent()) { + upgradeUsingModuleSetTag(upgradedCmHandle, moduleSetTag); + } else { + syncAndCreateSchemaSetAndAnchor(upgradedCmHandle); + } + setCmHandleModuleSetTag(upgradedCmHandle, moduleSetTag); + } + + private void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) { final Collection allModuleReferencesFromCmHandle = dmiModelOperations.getModuleReferences(yangModelCmHandle); @@ -73,8 +106,8 @@ public class ModuleSyncService { final Map newModuleNameToContentMap, final Collection allModuleReferencesFromCmHandle) { final String schemaSetAndAnchorName = yangModelCmHandle.getId(); - cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName, - newModuleNameToContentMap, allModuleReferencesFromCmHandle); + cpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, + schemaSetAndAnchorName, newModuleNameToContentMap, allModuleReferencesFromCmHandle); cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName, schemaSetAndAnchorName); } @@ -94,4 +127,41 @@ public class ModuleSyncService { } } + private Optional getFirstReadyDataNodeWithModuleSetTag(final String moduleSetTag) { + final List dataNodes = StringUtils.isNotBlank(moduleSetTag) ? cmHandleQueries + .queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='" + moduleSetTag + "']", + FetchDescendantsOption.OMIT_DESCENDANTS) : Collections.emptyList(); + return dataNodes.stream().filter(dataNode -> { + final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(dataNode.getXpath()); + return cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY); + }).findFirst(); + } + + private void setCmHandleModuleSetTag(final YangModelCmHandle upgradedCmHandle, final String moduleSetTag) { + final Map> dmiRegistryProperties = new HashMap<>(1); + final Map cmHandleProperties = new HashMap<>(2); + cmHandleProperties.put("id", upgradedCmHandle.getId()); + cmHandleProperties.put("module-set-tag", moduleSetTag); + dmiRegistryProperties.put("cm-handles", cmHandleProperties); + cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT, + jsonObjectMapper.asJsonString(dmiRegistryProperties), OffsetDateTime.now()); + } + + private void upgradeUsingModuleSetTag(final YangModelCmHandle upgradedCmHandle, final String moduleSetTag) { + log.info("Found cm handle having module set tag: {}", moduleSetTag); + final Collection moduleReferencesFromExistingCmHandle = + cpsModuleService.getYangResourcesModuleReferences(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR); + final String upgradedSchemaSetAndAnchorName = upgradedCmHandle.getId(); + final Map noNewModules = Collections.emptyMap(); + cpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, + upgradedSchemaSetAndAnchorName, noNewModules, moduleReferencesFromExistingCmHandle); + } + + private static String extractModuleSetTag(final CompositeState compositeState) { + return compositeState.getLockReason() != null && compositeState.getLockReason().getLockReasonCategory() + == LockReasonCategory.MODULE_UPGRADE + ? Arrays.stream(compositeState.getLockReason().getDetails().split(":")).toList().get(1).trim() + : StringUtils.EMPTY; + } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java index 7306f7174..c19dbeb90 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java @@ -60,7 +60,8 @@ public class ModuleSyncTasks { public CompletableFuture performModuleSync(final Collection cmHandlesAsDataNodes, final AtomicInteger batchCounter) { try { - final Map cmHandelStatePerCmHandle = new HashMap<>(); + final Map cmHandelStatePerCmHandle + = new HashMap<>(cmHandlesAsDataNodes.size()); for (final DataNode cmHandleAsDataNode : cmHandlesAsDataNodes) { final String cmHandleId = String.valueOf(cmHandleAsDataNode.getLeaves().get("id")); final YangModelCmHandle yangModelCmHandle = @@ -68,16 +69,18 @@ public class ModuleSyncTasks { final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); try { moduleSyncService.deleteSchemaSetIfExists(cmHandleId); - moduleSyncService.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle); + moduleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle); + yangModelCmHandle.getCompositeState().setLockReason(null); cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.READY); } catch (final Exception e) { - log.warn("Processing of {} module sync failed due to reason {}.", cmHandleId, e.getMessage()); + log.warn("Processing of {} module sync failed due to reason {}.", + cmHandleId, e.getMessage()); syncUtils.updateLockReasonDetailsAndAttempts(compositeState, LockReasonCategory.MODULE_SYNC_FAILED, e.getMessage()); setCmHandleStateLocked(yangModelCmHandle, compositeState.getLockReason()); cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED); } - log.info("{} is now in {} state", cmHandleId, compositeState.getCmHandleState().name()); + log.info("{} is now in {} state", cmHandleId, cmHandelStatePerCmHandle.get(yangModelCmHandle).name()); } lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandelStatePerCmHandle); } finally { @@ -96,7 +99,7 @@ public class ModuleSyncTasks { final Map cmHandleStatePerCmHandle = new HashMap<>(failedCmHandles.size()); for (final YangModelCmHandle failedCmHandle : failedCmHandles) { final CompositeState compositeState = failedCmHandle.getCompositeState(); - final boolean isReadyForRetry = syncUtils.needsModuleSyncRetry(compositeState); + final boolean isReadyForRetry = syncUtils.needsModuleSyncRetryOrUpgrade(compositeState); log.info("Retry for cmHandleId : {} is {}", failedCmHandle.getId(), isReadyForRetry); if (isReadyForRetry) { final String resetCmHandleId = failedCmHandle.getId(); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java index 6ba52ee16..75781eb1b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java @@ -90,7 +90,7 @@ public class ModuleSyncWatchdog { @Scheduled(fixedDelayString = "${ncmp.timers.locked-modules-sync.sleep-time-ms:300000}") public void resetPreviouslyFailedCmHandles() { log.info("Processing module sync retry-watchdog waking up."); - final List failedCmHandles = syncUtils.getModuleSyncFailedCmHandles(); + final List failedCmHandles = syncUtils.getCmHandlesThatFailedModelSyncOrUpgrade(); log.info("Retrying {} cmHandles", failedCmHandles.size()); moduleSyncTasks.resetFailedCmHandles(failedCmHandles); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/SyncUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/SyncUtils.java index c50bd4278..ab85c2127 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/SyncUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/SyncUtils.java @@ -35,7 +35,6 @@ import java.util.Map; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; @@ -100,13 +99,14 @@ public class SyncUtils { } /** - * Query data nodes for cm handles with an "LOCKED" cm handle state with reason MODULE_SYNC_FAILED". + * Query data nodes for cm handles with an "LOCKED" cm handle state with reason. * * @return a random LOCKED yang model cm handle, return null if not found */ - public List getModuleSyncFailedCmHandles() { - final List lockedCmHandlesAsDataNodeList = cmHandleQueries.queryCmHandleDataNodesByCpsPath( - "//lock-reason[@reason=\"MODULE_SYNC_FAILED\"]", + public List getCmHandlesThatFailedModelSyncOrUpgrade() { + final List lockedCmHandlesAsDataNodeList + = cmHandleQueries.queryCmHandleAncestorsByCpsPath( + "//lock-reason[@reason=\"MODULE_SYNC_FAILED\" or @reason=\"MODULE_UPGRADE\"]", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); return convertCmHandlesDataNodesToYangModelCmHandles(lockedCmHandlesAsDataNodeList); } @@ -139,32 +139,39 @@ public class SyncUtils { * @param compositeState the composite state currently in the locked state * @return if the retry mechanism should be attempted */ - public boolean needsModuleSyncRetry(final CompositeState compositeState) { - final OffsetDateTime time = - OffsetDateTime.parse(compositeState.getLastUpdateTime(), - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); - final Matcher matcher = retryAttemptPattern.matcher(compositeState.getLockReason().getDetails()); + public boolean needsModuleSyncRetryOrUpgrade(final CompositeState compositeState) { + final OffsetDateTime time = OffsetDateTime.parse(compositeState.getLastUpdateTime(), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); + final CompositeState.LockReason lockReason = compositeState.getLockReason(); + final boolean failedDuringModuleSync = LockReasonCategory.MODULE_SYNC_FAILED - == compositeState.getLockReason().getLockReasonCategory(); - if (!failedDuringModuleSync) { - log.info("Locked for other reason"); - return false; - } - final int timeInMinutesUntilNextAttempt; - if (matcher.find()) { - timeInMinutesUntilNextAttempt = (int) Math.pow(2, Integer.parseInt(matcher.group(1))); - } else { - timeInMinutesUntilNextAttempt = 1; - log.info("First Attempt: no current attempts found."); - } - final int timeSinceLastAttempt = (int) Duration.between(time, OffsetDateTime.now()).toMinutes(); - if (timeInMinutesUntilNextAttempt >= timeSinceLastAttempt) { - log.info("Time until next attempt is {} minutes: ", - timeInMinutesUntilNextAttempt - timeSinceLastAttempt); - return false; + == lockReason.getLockReasonCategory(); + final boolean moduleUpgrade = LockReasonCategory.MODULE_UPGRADE + == lockReason.getLockReasonCategory(); + + if (failedDuringModuleSync) { + final int timeInMinutesUntilNextAttempt; + final Matcher matcher = retryAttemptPattern.matcher(lockReason.getDetails()); + if (matcher.find()) { + timeInMinutesUntilNextAttempt = (int) Math.pow(2, Integer.parseInt(matcher.group(1))); + } else { + timeInMinutesUntilNextAttempt = 1; + log.info("First Attempt: no current attempts found."); + } + final int timeSinceLastAttempt = (int) Duration.between(time, OffsetDateTime.now()).toMinutes(); + if (timeInMinutesUntilNextAttempt >= timeSinceLastAttempt) { + log.info("Time until next attempt is {} minutes: ", + timeInMinutesUntilNextAttempt - timeSinceLastAttempt); + return false; + } + log.info("Retry due now"); + return true; + } else if (moduleUpgrade) { + log.info("Locked for module upgrade."); + return true; } - log.info("Retry due now"); - return true; + log.info("Locked for other reason"); + return false; } /** @@ -196,6 +203,6 @@ public class SyncUtils { final List cmHandlesAsDataNodeList) { return cmHandlesAsDataNodeList.stream() .map(cmHandle -> YangDataConverter.convertCmHandleToYangModel(cmHandle, - cmHandle.getLeaves().get("id").toString())).collect(Collectors.toList()); + cmHandle.getLeaves().get("id").toString())).toList(); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java index 8f76a45e0..b8edeccf2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -21,6 +21,8 @@ package org.onap.cps.ncmp.api.impl.operations; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA; import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING; import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ; @@ -30,7 +32,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NcmpEventResponseCode; +import org.onap.cps.ncmp.api.NcmpResponseStatus; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException; @@ -230,9 +232,7 @@ public class DmiDataOperations extends DmiOperations { final Map> groupsOutPerDmiServiceName) { - groupsOutPerDmiServiceName.entrySet().forEach(groupsOutPerDmiServiceNameEntry -> { - final String dmiServiceName = groupsOutPerDmiServiceNameEntry.getKey(); - final List dmiDataOperationRequestBodies = groupsOutPerDmiServiceNameEntry.getValue(); + groupsOutPerDmiServiceName.forEach((dmiServiceName, dmiDataOperationRequestBodies) -> { final String dmiDataOperationResourceUrl = getDmiServiceDataOperationRequestUrl(dmiServiceName, topicParamInQuery, requestId); sendDataOperationRequestToDmiService(dmiDataOperationResourceUrl, dmiDataOperationRequestBodies); @@ -261,18 +261,18 @@ public class DmiDataOperations extends DmiOperations { final String topicName = dataOperationResourceUrlParameters.get("topic").get(0); final String requestId = dataOperationResourceUrlParameters.get("requestId").get(0); - final MultiValueMap>> + final MultiValueMap>> cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>(); dmiDataOperationRequestBodies.forEach(dmiDataOperationRequestBody -> { final List cmHandleIds = dmiDataOperationRequestBody.getCmHandles().stream() - .map(CmHandle::getId).collect(Collectors.toList()); + .map(CmHandle::getId).toList(); if (throwable.getCause() instanceof HttpClientRequestException) { cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody, - Map.of(NcmpEventResponseCode.UNABLE_TO_READ_RESOURCE_DATA, cmHandleIds)); + Map.of(UNABLE_TO_READ_RESOURCE_DATA, cmHandleIds)); } else { cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody, - Map.of(NcmpEventResponseCode.DMI_SERVICE_NOT_RESPONDING, cmHandleIds)); + Map.of(DMI_SERVICE_NOT_RESPONDING, cmHandleIds)); } }); ResourceDataOperationRequestUtils.publishErrorMessageToClientTopic(topicName, requestId, diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java index 458c1b851..b6d74d980 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java @@ -20,14 +20,14 @@ package org.onap.cps.ncmp.api.impl.trustlevel; -import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent; - -import com.hazelcast.collection.ISet; import io.cloudevents.CloudEvent; import io.cloudevents.kafka.impl.KafkaHeaders; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper; +import org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @@ -36,7 +36,8 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class DeviceHeartbeatConsumer { - private final ISet untrustworthyCmHandlesSet; + private static final String CLOUD_EVENT_ID_HEADER_NAME = "ce_id"; + private final Map trustLevelPerCmHandle; /** * Listening the device heartbeats. @@ -47,23 +48,16 @@ public class DeviceHeartbeatConsumer { containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory") public void heartbeatListener(final ConsumerRecord deviceHeartbeatConsumerRecord) { - final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(deviceHeartbeatConsumerRecord.headers(), "ce_id"); + final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(deviceHeartbeatConsumerRecord.headers(), + CLOUD_EVENT_ID_HEADER_NAME); final DeviceTrustLevel deviceTrustLevel = - toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class); - - if (deviceTrustLevel == null || deviceTrustLevel.getTrustLevel() == null) { - log.warn("No or Invalid trust level defined"); - return; - } + CloudEventMapper.toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class); - if (deviceTrustLevel.getTrustLevel().equals(TrustLevel.NONE)) { - untrustworthyCmHandlesSet.add(cmHandleId); - log.debug("Added cmHandleId to untrustworthy set : {}", cmHandleId); - } else if (deviceTrustLevel.getTrustLevel().equals(TrustLevel.COMPLETE) && untrustworthyCmHandlesSet.contains( - cmHandleId)) { - untrustworthyCmHandlesSet.remove(cmHandleId); - log.debug("Removed cmHandleId from untrustworthy set : {}", cmHandleId); + if (cmHandleId != null && deviceTrustLevel != null) { + final String trustLevel = deviceTrustLevel.getData().getTrustLevel(); + trustLevelPerCmHandle.put(cmHandleId, TrustLevel.valueOf(trustLevel)); + log.debug("Added cmHandleId to trustLevelPerCmHandle map as {}:{}", cmHandleId, trustLevel); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java index f4254bb47..8d1f8e90f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java @@ -20,6 +20,16 @@ package org.onap.cps.ncmp.api.impl.trustlevel; +import lombok.Getter; + +@Getter public enum TrustLevel { - NONE, COMPLETE; + NONE(0), COMPLETE(99); + + private final int value; + + TrustLevel(final int value) { + this.value = value; + } + } \ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilter.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilter.java new file mode 100644 index 000000000..3b704ae4f --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilter.java @@ -0,0 +1,58 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.trustlevel; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +public class TrustLevelFilter implements Comparable { + + @EqualsAndHashCode.Include + private final TrustLevel targetTrustLevel; + private final Map trustLevelPerCmHandle; + + @Override + public int compareTo(@NonNull final TrustLevel other) { + return Integer.compare(this.targetTrustLevel.getValue(), other.getValue()); + } + + /** + * This method return cm handles that matches with given trust level. + * + * @return cm handle ids. + */ + public Collection getAllCmHandleIdsByTargetTrustLevel() { + final Collection resultCmHandleIds = new HashSet<>(); + trustLevelPerCmHandle.entrySet().forEach(cmHandleTrustLevelEntrySet -> { + if (compareTo(cmHandleTrustLevelEntrySet.getValue()) == 0) { + resultCmHandleIds.add(cmHandleTrustLevelEntrySet.getKey()); + } + }); + return resultCmHandleIds; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDog.java index dac32aa73..39f880257 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDog.java @@ -20,7 +20,7 @@ package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability; -import com.hazelcast.map.IMap; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; @@ -33,7 +33,7 @@ import org.springframework.stereotype.Service; @Service public class DMiPluginWatchDog { - private final IMap trustLevelPerDmiPlugin; + private final Map trustLevelPerDmiPlugin; private final DmiRestClient dmiRestClient; @@ -46,7 +46,7 @@ public class DMiPluginWatchDog { */ @Scheduled(fixedDelayString = "${ncmp.timers.trust-evel.dmi-availability-watchdog-ms:30000}") public void watchDmiPluginAliveness() { - trustLevelPerDmiPlugin.keySet().forEach((dmiPluginName) -> { + trustLevelPerDmiPlugin.keySet().forEach(dmiPluginName -> { final DmiPluginStatus dmiPluginStatus = dmiRestClient.getDmiPluginStatus(dmiPluginName); log.debug("Trust level for dmi-plugin: {} is {}", dmiPluginName, dmiPluginStatus.toString()); if (DmiPluginStatus.UP.equals(dmiPluginStatus)) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java index b1bb7f767..a59776036 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,8 @@ import lombok.Getter; public enum CmHandleQueryConditions { HAS_ALL_PROPERTIES("hasAllProperties"), HAS_ALL_MODULES("hasAllModules"), - WITH_CPS_PATH("cmHandleWithCpsPath"); + WITH_CPS_PATH("cmHandleWithCpsPath"), + WITH_TRUST_LEVEL("cmHandleWithTrustLevel"); public static final Collection ALL_CONDITION_NAMES = Arrays.stream(CmHandleQueryConditions.values()) .map(CmHandleQueryConditions::getConditionName).collect(Collectors.toList()); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java index 1b190759e..b6a04d367 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java @@ -86,7 +86,8 @@ public class YangDataConverter { (String) cmHandleDataNode.getLeaves().get("dmi-service-name"), (String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"), (String) cmHandleDataNode.getLeaves().get("dmi-model-service-name"), - ncmpServiceCmHandle + ncmpServiceCmHandle, + (String) cmHandleDataNode.getLeaves().get("module-set-tag") ); } @@ -105,7 +106,12 @@ public class YangDataConverter { return yangModelCmHandles; } - private static String extractCmHandleIdFromXpath(final String xpath) { + /** + * This method extract cm handle id from xpath of data node. + * @param xpath for data node of the cm handle + * @return cm handle Id + */ + public static String extractCmHandleIdFromXpath(final String xpath) { final Matcher matcher = cmHandleIdInXpathPattern.matcher(xpath); matcher.find(); return matcher.group(1); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java index 65cda9478..61da706c5 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java @@ -28,7 +28,7 @@ import java.util.Map; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NcmpEventResponseCode; +import org.onap.cps.ncmp.api.NcmpResponseStatus; import org.onap.cps.ncmp.api.impl.events.NcmpCloudEventBuilder; import org.onap.cps.ncmp.api.impl.operations.DmiDataOperation; import org.onap.cps.ncmp.events.async1_0_0.Data; @@ -51,7 +51,7 @@ public class DataOperationEventCreator { public static CloudEvent createDataOperationEvent(final String clientTopic, final String requestId, final MultiValueMap>> + Map>> cmHandleIdsPerResponseCodesPerOperation) { final DataOperationEvent dataOperationEvent = new DataOperationEvent(); final Data data = createPayloadFromDataOperationResponses(cmHandleIdsPerResponseCodesPerOperation); @@ -62,7 +62,7 @@ public class DataOperationEventCreator { } private static Data createPayloadFromDataOperationResponses(final MultiValueMap>> cmHandleIdsPerResponseCodesPerOperation) { + Map>> cmHandleIdsPerResponseCodesPerOperation) { final Data data = new Data(); final List responses = new ArrayList<>(); cmHandleIdsPerResponseCodesPerOperation.forEach((dmiDataOperation, cmHandleIdsPerResponseCodes) -> @@ -75,13 +75,13 @@ public class DataOperationEventCreator { private static List createResponseFromDataOperationResponses( final DmiDataOperation dmiDataOperation, - final Map> cmHandleIdsPerResponseCodeEntries) { + final Map> cmHandleIdsPerResponseCodeEntries) { final List responses = new ArrayList<>(); cmHandleIdsPerResponseCodeEntries.forEach((ncmpEventResponseCode, cmHandleIds) -> { final Response response = new Response(); response.setOperationId(dmiDataOperation.getOperationId()); - response.setStatusCode(ncmpEventResponseCode.getStatusCode()); - response.setStatusMessage(ncmpEventResponseCode.getStatusMessage()); + response.setStatusCode(ncmpEventResponseCode.getCode()); + response.setStatusMessage(ncmpEventResponseCode.getMessage()); response.setIds(cmHandleIds); response.setResourceIdentifier(dmiDataOperation.getResourceIdentifier()); response.setOptions(dmiDataOperation.getOptions()); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java index e95d4f4c7..e78f0901f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java @@ -20,6 +20,9 @@ package org.onap.cps.ncmp.api.impl.utils.data.operation; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY; + import io.cloudevents.CloudEvent; import java.util.ArrayList; import java.util.Collection; @@ -32,7 +35,7 @@ import java.util.stream.Collectors; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NcmpEventResponseCode; +import org.onap.cps.ncmp.api.NcmpResponseStatus; import org.onap.cps.ncmp.api.impl.events.EventsPublisher; import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; import org.onap.cps.ncmp.api.impl.operations.CmHandle; @@ -68,7 +71,7 @@ public class ResourceDataOperationRequestUtils { final Collection yangModelCmHandles) { final Map> dmiDataOperationsOutPerDmiServiceName = new HashMap<>(); - final MultiValueMap>> cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>(); final Set nonReadyCmHandleIdsLookup = filterAndGetNonReadyCmHandleIds(yangModelCmHandles); @@ -102,10 +105,10 @@ public class ResourceDataOperationRequestUtils { } populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation, DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn), - NcmpEventResponseCode.CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds); + CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds); populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation, DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn), - NcmpEventResponseCode.CM_HANDLES_NOT_READY, nonReadyCmHandleIds); + CM_HANDLES_NOT_READY, nonReadyCmHandleIds); } if (!cmHandleIdsPerResponseCodesPerOperation.isEmpty()) { publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation); @@ -124,7 +127,7 @@ public class ResourceDataOperationRequestUtils { public static void publishErrorMessageToClientTopic(final String clientTopic, final String requestId, final MultiValueMap>> + Map>> cmHandleIdsPerResponseCodesPerOperation) { final CloudEvent dataOperationCloudEvent = DataOperationEventCreator.createDataOperationEvent(clientTopic, requestId, cmHandleIdsPerResponseCodesPerOperation); @@ -175,13 +178,13 @@ public class ResourceDataOperationRequestUtils { } private static void populateCmHandleIdsPerOperationIdPerResponseCode(final MultiValueMap>> cmHandleIdsPerResponseCodesPerOperation, + Map>> cmHandleIdsPerResponseCodesPerOperation, final DmiDataOperation dmiDataOperation, - final NcmpEventResponseCode - ncmpEventResponseCode, + final NcmpResponseStatus + ncmpResponseStatus, final List cmHandleIds) { if (!cmHandleIds.isEmpty()) { - cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperation, Map.of(ncmpEventResponseCode, cmHandleIds)); + cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperation, Map.of(ncmpResponseStatus, cmHandleIds)); } } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java index 52fc81f50..d148f371b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java @@ -64,6 +64,9 @@ public class YangModelCmHandle { @JsonProperty("dmi-model-service-name") private String dmiModelServiceName; + @JsonProperty("module-set-tag") + private String moduleSetTag; + @JsonProperty("additional-properties") private List dmiProperties; @@ -102,12 +105,14 @@ public class YangModelCmHandle { public static YangModelCmHandle toYangModelCmHandle(final String dmiServiceName, final String dmiDataServiceName, final String dmiModelServiceName, - final NcmpServiceCmHandle ncmpServiceCmHandle) { + final NcmpServiceCmHandle ncmpServiceCmHandle, + final String moduleSetTag) { final YangModelCmHandle yangModelCmHandle = new YangModelCmHandle(); yangModelCmHandle.setId(ncmpServiceCmHandle.getCmHandleId()); yangModelCmHandle.setDmiServiceName(dmiServiceName); yangModelCmHandle.setDmiDataServiceName(dmiDataServiceName); yangModelCmHandle.setDmiModelServiceName(dmiModelServiceName); + yangModelCmHandle.setModuleSetTag(moduleSetTag); yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties())); yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties( ncmpServiceCmHandle.getPublicProperties())); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java index d5b27b61f..e007491ce 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022 Bell Canada - * Modifications Copyright (C) 2022 Nordix Foundation + * Modifications Copyright (C) 2022-2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,16 +21,16 @@ package org.onap.cps.ncmp.api.models; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR; + import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; import lombok.Builder; import lombok.Data; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.NcmpResponseStatus; +import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; @Data @Builder @@ -39,11 +39,9 @@ public class CmHandleRegistrationResponse { private final String cmHandle; private final Status status; - private RegistrationError registrationError; + private NcmpResponseStatus ncmpResponseStatus; private String errorText; - private static final Pattern cmHandleIdInXpathPattern = Pattern.compile("\\[@id='(.*?)']"); - /** * Creates a failure response based on exception. * @@ -56,7 +54,7 @@ public class CmHandleRegistrationResponse { return CmHandleRegistrationResponse.builder() .cmHandle(cmHandleId) .status(Status.FAILURE) - .registrationError(RegistrationError.UNKNOWN_ERROR) + .ncmpResponseStatus(UNKNOWN_ERROR) .errorText(exception.getMessage()).build(); } @@ -64,15 +62,15 @@ public class CmHandleRegistrationResponse { * Creates a failure response based on registration error. * * @param cmHandleId cmHandleId - * @param registrationError registrationError + * @param ncmpResponseStatus registration error code and status * @return CmHandleRegistrationResponse */ public static CmHandleRegistrationResponse createFailureResponse(final String cmHandleId, - final RegistrationError registrationError) { + final NcmpResponseStatus ncmpResponseStatus) { return CmHandleRegistrationResponse.builder().cmHandle(cmHandleId) .status(Status.FAILURE) - .registrationError(registrationError) - .errorText(registrationError.errorText) + .ncmpResponseStatus(ncmpResponseStatus) + .errorText(ncmpResponseStatus.getMessage()) .build(); } @@ -80,18 +78,18 @@ public class CmHandleRegistrationResponse { * Creates a failure response based on registration error. * * @param failedXpaths list of failed Xpaths - * @param registrationError enum describing the type of registration error + * @param ncmpResponseStatus enum describing the type of registration error * @return CmHandleRegistrationResponse */ public static List createFailureResponses(final Collection failedXpaths, - final RegistrationError registrationError) { + final NcmpResponseStatus ncmpResponseStatus) { final List cmHandleRegistrationResponses = new ArrayList<>(failedXpaths.size()); for (final String xpath : failedXpaths) { - final Matcher matcher = cmHandleIdInXpathPattern.matcher(xpath); - if (matcher.find()) { + try { + final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(xpath); cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createFailureResponse(matcher.group(1), registrationError)); - } else { + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, ncmpResponseStatus)); + } catch (IllegalArgumentException | IllegalStateException e) { log.warn("Unexpected xpath {}", xpath); } } @@ -109,7 +107,7 @@ public class CmHandleRegistrationResponse { final Exception exception) { return cmHandleIds.stream() .map(cmHandleId -> CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception)) - .collect(Collectors.toList()); + .toList(); } public static CmHandleRegistrationResponse createSuccessResponse(final String cmHandle) { @@ -118,23 +116,10 @@ public class CmHandleRegistrationResponse { } public static List createSuccessResponses(final List cmHandleIds) { - return cmHandleIds.stream().map(CmHandleRegistrationResponse::createSuccessResponse) - .collect(Collectors.toList()); + return cmHandleIds.stream().map(CmHandleRegistrationResponse::createSuccessResponse).toList(); } public enum Status { SUCCESS, FAILURE; } - - @RequiredArgsConstructor - public enum RegistrationError { - UNKNOWN_ERROR("00", "Unknown error"), - CM_HANDLE_ALREADY_EXIST("01", "cm-handle already exists"), - CM_HANDLE_DOES_NOT_EXIST("02", "cm-handle does not exist"), - CM_HANDLE_INVALID_ID("03", "cm-handle has an invalid character(s) in id"); - - public final String errorCode; - public final String errorText; - - } } 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 953b3c4e9..4615af61c 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 @@ -50,6 +50,8 @@ public class DmiPluginRegistration { private List removedCmHandles = Collections.emptyList(); + private UpgradedCmHandles upgradedCmHandles; + /** * Validates plugin service names. * @throws NcmpException if validation fails. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java index 8a3d26414..ee034176e 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022 Bell Canada + * Modifications Copyright (C) 2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,4 +32,5 @@ public class DmiPluginRegistrationResponse { private List createdCmHandles = Collections.emptyList(); private List updatedCmHandles = Collections.emptyList(); private List removedCmHandles = Collections.emptyList(); + private List upgradedCmHandles = Collections.emptyList(); } \ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java index c46a8c26d..0b50346f8 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java @@ -52,6 +52,9 @@ public class NcmpServiceCmHandle { @JsonSetter(nulls = Nulls.AS_EMPTY) private CompositeState compositeState; + @JsonSetter(nulls = Nulls.AS_EMPTY) + private String moduleSetTag; + /** * NcmpServiceCmHandle copy constructor. * @@ -63,5 +66,6 @@ public class NcmpServiceCmHandle { this.publicProperties = new LinkedHashMap<>(ncmpServiceCmHandle.getPublicProperties()); this.compositeState = ncmpServiceCmHandle.getCompositeState() != null ? new CompositeState( ncmpServiceCmHandle.getCompositeState()) : null; + this.moduleSetTag = ncmpServiceCmHandle.getModuleSetTag(); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/UpgradedCmHandles.java similarity index 70% rename from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java rename to cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/UpgradedCmHandles.java index 2ed4e4522..61cd99ac8 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/UpgradedCmHandles.java @@ -18,20 +18,19 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.trustlevel; +package org.onap.cps.ncmp.api.models; -import java.io.Serializable; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@AllArgsConstructor -@Data -@NoArgsConstructor -class DeviceTrustLevel implements Serializable { - - private static final long serialVersionUID = -1705715024067165212L; - - private TrustLevel trustLevel; +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.Collections; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +@Getter +@Setter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class UpgradedCmHandles { + private List cmHandles = Collections.emptyList(); + private String moduleSetTag; } + diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy index c2e2b91cf..7c410cc58 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy @@ -39,7 +39,7 @@ import spock.lang.Specification class NetworkCmProxyCmHandleQueryServiceSpec extends Specification { def cmHandleQueries = Mock(CmHandleQueries) - def partiallyMockedCmHandleQueries = Spy(CmHandleQueriesImpl) + def partiallyMockedCmHandleQueries = Spy(CmHandleQueries) def mockInventoryPersistence = Mock(InventoryPersistence) def dmiRegistry = new DataNode(xpath: NCMP_DMI_REGISTRY_PARENT, childDataNodes: createDataNodeList(['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4'])) @@ -53,7 +53,7 @@ class NetworkCmProxyCmHandleQueryServiceSpec extends Specification { def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']]) cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) and: 'the query get the cm handle datanodes excluding all descendants returns a datanode' - cmHandleQueries.queryCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])] + cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])] when: 'the query is executed for cm handle ids' def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) then: 'the correct expected cm handles ids are returned' @@ -66,7 +66,7 @@ class NetworkCmProxyCmHandleQueryServiceSpec extends Specification { def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']]) cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) and: 'cmHandleQueries throws a path parsing exception' - cmHandleQueries.queryCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> { throw thrownException } + cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> { throw thrownException } when: 'the query is executed for cm handle ids' objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) then: 'a data validation exception is thrown' @@ -106,6 +106,17 @@ class NetworkCmProxyCmHandleQueryServiceSpec extends Specification { 'No anchors are returned' | [] } + def 'Query cm handles with some trust level query parameters'() { + given: 'a trust level condition property' + def trustLevelQueryParameters = new CmHandleQueryServiceParameters() + def trustLevelConditionProperties = createConditionProperties('cmHandleWithTrustLevel', [['trustLevel': 'COMPLETE'] as Map]) + trustLevelQueryParameters.setCmHandleQueryParameters([trustLevelConditionProperties]) + when: 'the query is being executed' + objectUnderTest.queryCmHandleIds(trustLevelQueryParameters) + then: 'the query is being delegated to the cm handle query service with correct parameter' + 1 * cmHandleQueries.queryCmHandlesByTrustLevel(['trustLevel': 'COMPLETE'] as Map) + } + def 'Query cm handle details with module names when #scenario from query.'() { given: 'a modules condition property' def cmHandleQueryParameters = new CmHandleQueryServiceParameters() 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 46666b941..9e4737fff 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 @@ -21,6 +21,12 @@ package org.onap.cps.ncmp.api.impl +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR +import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status + import com.fasterxml.jackson.databind.ObjectMapper import com.hazelcast.map.IMap import org.onap.cps.api.CpsDataService @@ -45,12 +51,6 @@ import org.onap.cps.utils.JsonObjectMapper import spock.lang.Shared import spock.lang.Specification -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_ALREADY_EXIST -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_INVALID_ID -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status - class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { @Shared @@ -80,11 +80,11 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { when: 'registration is processed' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) then: 'cm-handles are removed first' - 1 * objectUnderTest.parseAndRemoveCmHandlesInDmiRegistration(*_) + 1 * objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) and: 'de-registered cm handle entry is removed from in progress map' 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2') then: 'cm-handles are created' - 1 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(*_) + 1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) then: 'cm-handles are updated' 1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) } @@ -100,10 +100,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> updateResponses and: 'create cm-handles can be processed successfully' def createdResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-1')] - objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(*_) >> createdResponses + objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) >> createdResponses and: 'delete cm-handles can be processed successfully' def removeResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-3')] - objectUnderTest.parseAndRemoveCmHandlesInDmiRegistration(*_) >> removeResponses + objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) >> removeResponses when: 'registration is processed' def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) then: 'response has values from all operations' @@ -120,7 +120,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { when: 'update registration and sync module is called with correct DMI plugin information' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'create cm handles registration and sync modules is called with the correct plugin information' - 1 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration) + 1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration) and: 'dmi is added to the trustLevel map' 1 * mockTrustLevelPerDmiPlugin.put(dmiPluginRegisteredName, TrustLevel.COMPLETE) where: @@ -141,7 +141,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def exceptionThrown = thrown(DmiRequestException.class) assert exceptionThrown.getMessage().contains(expectedMessageDetails) and: 'registration is not called' - 0 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration) + 0 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration) where: scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails 'empty DMI plugins' | '' | '' | '' || 'No DMI plugin service names' @@ -203,7 +203,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { response.createdCmHandles.each { assert it.cmHandle == 'cmhandle2' assert it.status == Status.FAILURE - assert it.registrationError == CM_HANDLE_ALREADY_EXIST + assert it.ncmpResponseStatus == CM_HANDLE_ALREADY_EXIST assert it.errorText == 'cm-handle already exists' } } @@ -221,7 +221,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { with(response.createdCmHandles[0]) { assert it.status == Status.FAILURE assert it.cmHandle == 'cmhandle' - assert it.registrationError == expectedError + assert it.ncmpResponseStatus == expectedError assert it.errorText == expectedErrorText } where: @@ -236,7 +236,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { and: 'cm-handle updates can be processed successfully' def updateOperationResponse = [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-1'), CmHandleRegistrationResponse.createFailureResponse('cm-handle-2', new Exception("Failed")), - CmHandleRegistrationResponse.createFailureResponse('cm-handle-3', CM_HANDLE_DOES_NOT_EXIST), + CmHandleRegistrationResponse.createFailureResponse('cm-handle-3', CM_HANDLES_NOT_FOUND), CmHandleRegistrationResponse.createFailureResponse('cm handle 4', CM_HANDLE_INVALID_ID)] mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(_) >> updateOperationResponse when: 'registration is updated' @@ -310,7 +310,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { and: '2nd cm-handle deletion fails' with(response.removedCmHandles[1]) { assert it.status == Status.FAILURE - assert it.registrationError == UNKNOWN_ERROR + assert it.ncmpResponseStatus == UNKNOWN_ERROR assert it.errorText == 'Failed' assert it.cmHandle == 'cmhandle2' } @@ -344,7 +344,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { assert it.status == Status.FAILURE assert it.cmHandle == 'cmhandle' assert it.errorText == 'Failed' - assert it.registrationError == UNKNOWN_ERROR + assert it.ncmpResponseStatus == UNKNOWN_ERROR } } @@ -364,16 +364,16 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { with(response.removedCmHandles[0]) { assert it.status == Status.FAILURE assert it.cmHandle == 'cmhandle' - assert it.registrationError == expectedError + assert it.ncmpResponseStatus == expectedError assert it.errorText == expectedErrorText } and: 'the cm handle state is not updated to "DELETED"' 0 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_, CmHandleState.DELETED) where: - scenario | cmHandleId | deleteListElementException || expectedError | expectedErrorText - 'cm-handle does not exist' | 'cmhandle' | new DataNodeNotFoundException('', '', '') || CM_HANDLE_DOES_NOT_EXIST | 'cm-handle does not exist' - 'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException('', '') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id' - 'an unexpected exception' | 'cmhandle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' + scenario | cmHandleId | deleteListElementException || expectedError | expectedErrorText + 'cm-handle does not exist' | 'cmhandle' | new DataNodeNotFoundException('', '', '') || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found' + 'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException('', '') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id' + 'an unexpected exception' | 'cmhandle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' } def getObjectUnderTest() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 01a0600af..af65cfc1a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -76,7 +76,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService) def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler) def stubModuleSyncStartedOnCmHandles = Stub(IMap) - def stubTrustLevelPerDmiPlugin = Stub(IMap) + def stubTrustLevelPerDmiPlugin = Stub(Map) def NO_TOPIC = null def NO_REQUEST_ID = null @@ -261,7 +261,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle] when: 'parse and create cm handle in dmi registration then sync module' - objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(mockDmiPluginRegistration) + objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration) then: 'system persists the cm handle state' 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args -> { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy index 610400f75..6439f0b58 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy @@ -24,13 +24,13 @@ package org.onap.cps.ncmp.api.impl import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR +import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence import org.onap.cps.spi.exceptions.DataValidationException -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_INVALID_ID -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.spi.exceptions.DataNodeNotFoundException import org.onap.cps.spi.model.DataNode @@ -131,14 +131,14 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { with(response.get(0)) { assert it.status == Status.FAILURE assert it.cmHandle == cmHandleId - assert it.registrationError == expectedError + assert it.ncmpResponseStatus == expectedError assert it.errorText == expectedErrorText } where: - scenario | cmHandleId | exception || expectedError | expectedErrorText - 'Cm Handle does not exist' | 'cmHandleId' | new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) || CM_HANDLE_DOES_NOT_EXIST | 'cm-handle does not exist' - 'Unknown' | 'cmHandleId' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' - 'Invalid cm handle id' | 'cmHandleId with spaces' | new DataValidationException('Name Validation Error.', cmHandleId + 'contains an invalid character') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id' + scenario | cmHandleId | exception || expectedError | expectedErrorText + 'Cm Handle does not exist' | 'cmHandleId' | new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found' + 'Unknown' | 'cmHandleId' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' + 'Invalid cm handle id' | 'cmHandleId with spaces' | new DataValidationException('Name Validation Error.', cmHandleId + 'contains an invalid character') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id' } def 'Multiple update operations in a single request'() { @@ -166,8 +166,8 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { with(cmHandleResponseList.get(1)) { assert it.status == Status.FAILURE assert it.cmHandle == cmHandleId - assert it.registrationError == CM_HANDLE_DOES_NOT_EXIST - assert it.errorText == "cm-handle does not exist" + assert it.ncmpResponseStatus == CM_HANDLES_NOT_FOUND + assert it.errorText == 'cm handle id(s) not found' } then: 'the replace list method is called twice' 2 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfigSpec.groovy index 6184a9722..3eff96d79 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfigSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfigSpec.groovy @@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.config.embeddedcache import com.hazelcast.config.Config import com.hazelcast.core.Hazelcast -import com.hazelcast.map.IMap import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest @@ -32,7 +31,10 @@ import spock.lang.Specification class TrustLevelCacheConfigSpec extends Specification { @Autowired - private IMap trustLevelPerDmiPlugin + private Map trustLevelPerDmiPlugin + + @Autowired + private Map trustLevelPerCmHandle def 'Hazelcast cache for trust level per dmi plugin'() { expect: 'system is able to create an instance of the trust level per dmi plugin cache' @@ -43,23 +45,29 @@ class TrustLevelCacheConfigSpec extends Specification { assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceTrustLevelPerDmiPluginMap') } - def 'Verify Trust Level Per Dmi Plugin Cache for basic hazelcast map operations'() { - when: 'the key inserted into Trust Level Per Dmi Plugin Cache' - trustLevelPerDmiPlugin.put('dmi1', TrustLevel.COMPLETE) - trustLevelPerDmiPlugin.put('dmi2', TrustLevel.NONE) - then: 'the value for each dmi can be retrieved' - assert trustLevelPerDmiPlugin.get('dmi1') == TrustLevel.COMPLETE - assert trustLevelPerDmiPlugin.get('dmi2') == TrustLevel.NONE + def 'Hazelcast cache for trust level per cm handle'() { + expect: 'system is able to create an instance of the trust level per cm handle cache' + assert null != trustLevelPerCmHandle + and: 'there is at least 1 instance' + assert Hazelcast.allHazelcastInstances.size() > 0 + and: 'Hazelcast cache instance for trust level is present' + assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceTrustLevelPerCmHandleMap') } - def 'Verify configs for Distributed Caches'(){ - given: 'the Trust Level Per Dmi Plugin Cache config' - def trustLevelDmiPerPluginCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelcastInstanceTrustLevelPerDmiPluginMap').config - def trustLevelDmiPerPluginCacheMapConfig = trustLevelDmiPerPluginCacheConfig.mapConfigs.get('trustLevelPerDmiPluginCacheConfig') - expect: 'system created instance with correct config' - assert trustLevelDmiPerPluginCacheConfig.clusterName == 'cps-and-ncmp-test-caches' - assert trustLevelDmiPerPluginCacheMapConfig.backupCount == 3 - assert trustLevelDmiPerPluginCacheMapConfig.asyncBackupCount == 3 + def 'Trust level cache configurations: #scenario'() { + when: 'retrieving the cache config for trustLevel' + def cacheConfig = Hazelcast.getHazelcastInstanceByName(hazelcastInstanceName).config + then: 'the cache config has the right cluster' + assert cacheConfig.clusterName == 'cps-and-ncmp-test-caches' + when: 'retrieving the map config for trustLevel' + def mapConfig = cacheConfig.mapConfigs.get(hazelcastMapConfigName) + then: 'the map config has the correct backup counts' + assert mapConfig.backupCount == 3 + assert mapConfig.asyncBackupCount == 3 + where: 'the following caches are used' + scenario | hazelcastInstanceName | hazelcastMapConfigName + 'cmhandle map' | 'hazelcastInstanceTrustLevelPerCmHandleMap' | 'trustLevelPerCmHandleCacheConfig' + 'dmi plugin map' | 'hazelcastInstanceTrustLevelPerDmiPluginMap' | 'trustLevelPerDmiPluginCacheConfig' } def 'Verify deployment network configs for Distributed Caches'() { @@ -70,6 +78,14 @@ class TrustLevelCacheConfigSpec extends Specification { assert !trustLevelDmiPerPluginCacheConfig.join.kubernetesConfig.enabled } + def 'Verify deployment network configs for Cm Handle Distributed Caches'() { + given: 'the Trust Level Per Cm Handle Cache config' + def trustLevelPerCmHandlePluginCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelcastInstanceTrustLevelPerCmHandleMap').config.networkConfig + expect: 'system created instance with correct config' + assert trustLevelPerCmHandlePluginCacheConfig.join.autoDetectionConfig.enabled + assert !trustLevelPerCmHandlePluginCacheConfig.join.kubernetesConfig.enabled + } + def 'Verify network config'() { given: 'Synchronization config object and test configuration' def objectUnderTest = new TrustLevelCacheConfig() diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisherSpec.groovy index cc1419519..a0567cb4c 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisherSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisherSpec.groovy @@ -20,11 +20,15 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription +import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESSFULLY_APPLIED_SUBSCRIPTION +import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_PENDING +import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_NOT_APPLICABLE +import static org.onap.cps.ncmp.api.NcmpResponseStatus.PARTIALLY_APPLIED_SUBSCRIPTION + import com.fasterxml.jackson.databind.ObjectMapper import io.cloudevents.CloudEvent import io.cloudevents.core.builder.CloudEventBuilder import org.mapstruct.factory.Mappers -import org.onap.cps.ncmp.api.NcmpEventResponseCode import org.onap.cps.ncmp.api.impl.events.EventsPublisher import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence import org.onap.cps.ncmp.api.impl.utils.DataNodeBaseSpec @@ -98,9 +102,9 @@ class CmSubscriptionNcmpOutEventPublisherSpec extends DataNodeBaseSpec { then: 'the result will be equal to ncmp out event' expectedResult == ncmpOutEvent where: 'the following values are used' - scenario | ncmpEventResponseCode || statusMessage || statusCode - 'is full outcome' | NcmpEventResponseCode.SUCCESSFULLY_APPLIED_SUBSCRIPTION || 'successfully applied subscription' || 1 - 'is partial outcome' | NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION || 'partially applied subscription' || 104 + scenario | ncmpEventResponseCode || statusMessage || statusCode + 'is full outcome' | SUCCESSFULLY_APPLIED_SUBSCRIPTION || 'successfully applied subscription' || 1 + 'is partial outcome' | PARTIALLY_APPLIED_SUBSCRIPTION || 'partially applied subscription' || 104 } def 'Check cm handle id to status map to see if it is a full outcome response'() { @@ -109,16 +113,16 @@ class CmSubscriptionNcmpOutEventPublisherSpec extends DataNodeBaseSpec { then: 'the result will be as expected' response == expectedOutcomeResponseDecision where: 'the following values are used' - scenario | cmHandleIdToStatusAndDetailsAsMap || expectedOutcomeResponseDecision - 'The map contains PENDING status' | [CMHandle1: [details:'Subscription forwarded to dmi plugin',status:'PENDING'] as Map] as Map || NcmpEventResponseCode.SUBSCRIPTION_PENDING - 'The map contains ACCEPTED status' | [CMHandle1: [details:'',status:'ACCEPTED'] as Map] as Map || NcmpEventResponseCode.SUCCESSFULLY_APPLIED_SUBSCRIPTION - 'The map contains REJECTED status' | [CMHandle1: [details:'Cm handle does not exist',status:'REJECTED'] as Map] as Map || NcmpEventResponseCode.SUBSCRIPTION_NOT_APPLICABLE - 'The map contains PENDING and PENDING statuses' | [CMHandle1: [details:'Some details',status:'PENDING'] as Map, CMHandle2: [details:'Some details',status:'PENDING'] as Map as Map] as Map || NcmpEventResponseCode.SUBSCRIPTION_PENDING - 'The map contains ACCEPTED and ACCEPTED statuses' | [CMHandle1: [details:'',status:'ACCEPTED'] as Map, CMHandle2: [details:'',status:'ACCEPTED'] as Map as Map] as Map || NcmpEventResponseCode.SUCCESSFULLY_APPLIED_SUBSCRIPTION - 'The map contains REJECTED and REJECTED statuses' | [CMHandle1: [details:'Reject details',status:'REJECTED'] as Map, CMHandle2: [details:'Reject details',status:'REJECTED'] as Map as Map] as Map || NcmpEventResponseCode.SUBSCRIPTION_NOT_APPLICABLE - 'The map contains PENDING and ACCEPTED statuses' | [CMHandle1: [details:'Some details',status:'PENDING'] as Map, CMHandle2: [details:'',status:'ACCEPTED'] as Map as Map] as Map || NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION - 'The map contains REJECTED and ACCEPTED statuses' | [CMHandle1: [details:'Cm handle does not exist',status:'REJECTED'] as Map, CMHandle2: [details:'',status:'ACCEPTED'] as Map as Map] as Map || NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION - 'The map contains PENDING and REJECTED statuses' | [CMHandle1: [details:'Subscription forwarded to dmi plugin',status:'PENDING'] as Map, CMHandle2: [details:'Cm handle does not exist',status:'REJECTED'] as Map as Map] as Map || NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION + scenario | cmHandleIdToStatusAndDetailsAsMap || expectedOutcomeResponseDecision + 'The map contains PENDING status' | [CMHandle1: [details: 'Subscription forwarded to dmi plugin', status: 'PENDING'] as Map] as Map || SUBSCRIPTION_PENDING + 'The map contains ACCEPTED status' | [CMHandle1: [details: '', status: 'ACCEPTED'] as Map] as Map || SUCCESSFULLY_APPLIED_SUBSCRIPTION + 'The map contains REJECTED status' | [CMHandle1: [details: 'Cm handle does not exist', status: 'REJECTED'] as Map] as Map || SUBSCRIPTION_NOT_APPLICABLE + 'The map contains PENDING and PENDING statuses' | [CMHandle1: [details: 'Some details', status: 'PENDING'] as Map, CMHandle2: [details: 'Some details', status: 'PENDING'] as Map as Map] as Map || SUBSCRIPTION_PENDING + 'The map contains ACCEPTED and ACCEPTED statuses' | [CMHandle1: [details: '', status: 'ACCEPTED'] as Map, CMHandle2: [details: '', status: 'ACCEPTED'] as Map as Map] as Map || SUCCESSFULLY_APPLIED_SUBSCRIPTION + 'The map contains REJECTED and REJECTED statuses' | [CMHandle1: [details: 'Reject details', status: 'REJECTED'] as Map, CMHandle2: [details: 'Reject details', status: 'REJECTED'] as Map as Map] as Map || SUBSCRIPTION_NOT_APPLICABLE + 'The map contains PENDING and ACCEPTED statuses' | [CMHandle1: [details: 'Some details', status: 'PENDING'] as Map, CMHandle2: [details: '', status: 'ACCEPTED'] as Map as Map] as Map || PARTIALLY_APPLIED_SUBSCRIPTION + 'The map contains REJECTED and ACCEPTED statuses' | [CMHandle1: [details: 'Cm handle does not exist', status: 'REJECTED'] as Map, CMHandle2: [details: '', status: 'ACCEPTED'] as Map as Map] as Map || PARTIALLY_APPLIED_SUBSCRIPTION + 'The map contains PENDING and REJECTED statuses' | [CMHandle1: [details: 'Subscription forwarded to dmi plugin', status: 'PENDING'] as Map, CMHandle2: [details: 'Cm handle does not exist', status: 'REJECTED'] as Map as Map] as Map || PARTIALLY_APPLIED_SUBSCRIPTION } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapperSpec.groovy index 380aea405..e6d383128 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapperSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapperSpec.groovy @@ -34,17 +34,30 @@ class CloudEventMapperSpec extends Specification { @Autowired JsonObjectMapper jsonObjectMapper - def 'Cloud event to Target event type when it is #scenario'() { - expect: 'Events mapped correctly' - assert mappedCloudEvent == (CloudEventMapper.toTargetEvent(testCloudEvent(), targetClass) != null) + def 'Cloud event to target event type'() { + given: 'a cloud event with valid payload' + def cloudEvent = testCloudEvent(new CmSubscriptionNcmpInEvent()) + when: 'the cloud event mapped to target event' + def result = CloudEventMapper.toTargetEvent((cloudEvent), CmSubscriptionNcmpInEvent.class) + then: 'the cloud event is mapped' + assert result instanceof CmSubscriptionNcmpInEvent + } + + def 'Cloud event to target event type when it is #scenario'() { + given: 'a cloud event with invalid payload' + def cloudEvent = testCloudEvent(payload) + when: 'the cloud event mapped to target event' + def result = CloudEventMapper.toTargetEvent(cloudEvent, CmSubscriptionNcmpInEvent.class) + then: 'result is null' + assert result == null where: 'below are the scenarios' - scenario | targetClass || mappedCloudEvent - 'valid concrete type' | CmSubscriptionNcmpInEvent.class || true - 'invalid concrete type' | ArrayList.class || false + scenario | payload + 'invalid payload type' | ArrayList.class + 'without payload' | null } - def testCloudEvent() { - return CloudEventBuilder.v1().withData(jsonObjectMapper.asJsonBytes(new CmSubscriptionNcmpInEvent())) + def testCloudEvent(payload) { + return CloudEventBuilder.v1().withData(jsonObjectMapper.asJsonBytes(payload)) .withId("cmhandle1") .withSource(URI.create('test-source')) .withDataSchema(URI.create('test')) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy index 76ef23fe5..39c4fe6ee 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy @@ -21,8 +21,16 @@ package org.onap.cps.ncmp.api.impl.operations +import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL +import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING +import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE +import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ +import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE +import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA +import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING + import com.fasterxml.jackson.databind.ObjectMapper -import org.onap.cps.ncmp.api.NcmpEventResponseCode import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration import org.onap.cps.ncmp.api.impl.events.EventsPublisher import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException @@ -41,13 +49,6 @@ import org.springframework.http.HttpStatus import spock.lang.Shared import java.util.concurrent.TimeoutException -import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL -import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING -import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE -import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ -import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE -import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent - @SpringBootTest @ContextConfiguration(classes = [EventsPublisher, CpsApplicationContext, NcmpConfiguration.DmiProperties, DmiDataOperations]) class DmiDataOperationsSpec extends DmiOperationsBaseSpec { @@ -128,12 +129,12 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { def eventDataValue = extractDataValue(actualDataOperationCloudEvent) assert eventDataValue.operationId == dmiDataOperation.operationId assert eventDataValue.ids == dmiDataOperation.cmHandles.id - assert eventDataValue.statusCode == responseCode.statusCode - assert eventDataValue.statusMessage == responseCode.statusMessage + assert eventDataValue.statusCode == responseCode.code + assert eventDataValue.statusMessage == responseCode.message where: 'the following exceptions are occurred' scenario | exception || responseCode - 'http client request exception' | new HttpClientRequestException('error-message', 'error-details', HttpStatus.SERVICE_UNAVAILABLE.value()) || NcmpEventResponseCode.UNABLE_TO_READ_RESOURCE_DATA - 'timeout exception' | new TimeoutException() || NcmpEventResponseCode.DMI_SERVICE_NOT_RESPONDING + 'http client request exception' | new HttpClientRequestException('error-message', 'error-details', HttpStatus.SERVICE_UNAVAILABLE.value()) || UNABLE_TO_READ_RESOURCE_DATA + 'timeout exception' | new TimeoutException() || DMI_SERVICE_NOT_RESPONDING } def 'call get all resource data.'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy index 48de23dca..80778b9c7 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy @@ -21,81 +21,70 @@ package org.onap.cps.ncmp.api.impl.trustlevel import com.fasterxml.jackson.databind.ObjectMapper -import com.hazelcast.collection.ISet +import com.hazelcast.map.IMap import io.cloudevents.CloudEvent import io.cloudevents.core.builder.CloudEventBuilder import org.apache.kafka.clients.consumer.ConsumerRecord +import org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel import org.onap.cps.utils.JsonObjectMapper +import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import spock.lang.Specification @SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) class DeviceHeartbeatConsumerSpec extends Specification { - def mockUntrustworthyCmHandlesSet = Mock(ISet) + def mockTrustLevelPerCmHandle = Mock(Map) + + def objectUnderTest = new DeviceHeartbeatConsumer(mockTrustLevelPerCmHandle) def objectMapper = new ObjectMapper() - def objectUnderTest = new DeviceHeartbeatConsumer(mockUntrustworthyCmHandlesSet) + @Autowired + JsonObjectMapper jsonObjectMapper + + def static trustLevelString = '{"data":{"trustLevel": "COMPLETE"}}' - def 'Operations to be done in an empty untrustworthy set for #scenario'() { - given: 'an event with trustlevel as #trustLevel' - def incomingEvent = testCloudEvent(trustLevel) - and: 'transformed as a kafka record' - def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'cmhandle1', incomingEvent) + def 'Consume a trustlevel event'() { + given: 'an event from dmi with trust level complete' + def payload = jsonObjectMapper.convertJsonString(trustLevelString, DeviceTrustLevel.class) + def eventFromDmi = createTrustLevelEvent(payload) + and: 'transformed to a consumer record with a cloud event id (ce_id)' + def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'sample-message-key', eventFromDmi) consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1')) when: 'the event is consumed' objectUnderTest.heartbeatListener(consumerRecord) - then: 'untrustworthy cmhandles are stored' - untrustworthyCmHandlesSetInvocationForAdd * mockUntrustworthyCmHandlesSet.add(_) - and: 'trustworthy cmHandles will be removed from untrustworthy set' - untrustworthyCmHandlesSetInvocationForContains * mockUntrustworthyCmHandlesSet.contains(_) - - where: 'below scenarios are applicable' - scenario | trustLevel || untrustworthyCmHandlesSetInvocationForAdd | untrustworthyCmHandlesSetInvocationForContains - 'None trust' | TrustLevel.NONE || 1 | 0 - 'Complete trust' | TrustLevel.COMPLETE || 0 | 1 + then: 'cm handles are stored with correct trust level' + 1 * mockTrustLevelPerCmHandle.put('"cmhandle1"', TrustLevel.COMPLETE) } - def 'Invalid trust'() { - when: 'we provide an invalid trust in the event' - def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'cmhandle1', testCloudEvent(null)) - consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1')) + def 'Consume trustlevel event without cloud event id'() { + given: 'an event from dmi' + def payload = jsonObjectMapper.convertJsonString(trustLevelString, DeviceTrustLevel.class) + def eventFromDmi = createTrustLevelEvent(payload) + and: 'transformed to a consumer record WITHOUT Cloud event ID (ce_id)' + def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'sample-message-key', eventFromDmi) + when: 'the event is consumed' objectUnderTest.heartbeatListener(consumerRecord) - then: 'no interaction with the untrustworthy cmhandles set' - 0 * mockUntrustworthyCmHandlesSet.add(_) - 0 * mockUntrustworthyCmHandlesSet.contains(_) - 0 * mockUntrustworthyCmHandlesSet.remove(_) - and: 'control flow returns without any exception' - noExceptionThrown() - + then: 'no cm handle has been stored in the map' + 0 * mockTrustLevelPerCmHandle.put(*_) } - def 'Remove trustworthy cmhandles from untrustworthy cmhandles set'() { - given: 'an event with COMPLETE trustlevel' - def incomingEvent = testCloudEvent(TrustLevel.COMPLETE) - and: 'transformed as a kafka record' - def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'cmhandle1', incomingEvent) - consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1')) - and: 'untrustworthy cmhandles set contains cmhandle1' - 1 * mockUntrustworthyCmHandlesSet.contains(_) >> true + def 'Consume a trust level event without payload'() { + given: 'a consumer record with ce_id header but without payload' + def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'cmhandle1', createTrustLevelEvent(null)) + consumerRecord.headers().add('some_other_header_value', objectMapper.writeValueAsBytes('cmhandle1')) when: 'the event is consumed' objectUnderTest.heartbeatListener(consumerRecord) - then: 'cmhandle removed from untrustworthy cmhandles set' - 1 * mockUntrustworthyCmHandlesSet.remove(_) >> { - args -> - { - args[0].equals('cmhandle1') - } - } - + then: 'no cm handle has been stored in the map' + 0 * mockTrustLevelPerCmHandle.put(*_) } - def testCloudEvent(trustLevel) { - return CloudEventBuilder.v1().withData(objectMapper.writeValueAsBytes(new DeviceTrustLevel(trustLevel))) + def createTrustLevelEvent(eventPayload) { + return CloudEventBuilder.v1().withData(objectMapper.writeValueAsBytes(eventPayload)) .withId("cmhandle1") .withSource(URI.create('DMI')) .withDataSchema(URI.create('test')) - .withType('org.onap.cm.events.trustlevel-notification') + .withType('org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel') .build() } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilterSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilterSpec.groovy new file mode 100644 index 000000000..8f6621d24 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilterSpec.groovy @@ -0,0 +1,41 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.trustlevel + + +import spock.lang.Specification + +class TrustLevelFilterSpec extends Specification { + + def targetTrustLevel = TrustLevel.COMPLETE + + def trustLevelPerCmHandle = [ 'my completed cm handle': TrustLevel.COMPLETE, 'my untrusted cm handle': TrustLevel.NONE ] + + def objectUnderTest = new TrustLevelFilter(targetTrustLevel, trustLevelPerCmHandle) + + def 'Obtain cm handle ids by a given trust level value'() { + when: 'cm handles are retrieved' + def result = objectUnderTest.getAllCmHandleIdsByTargetTrustLevel() + then: 'the result only contains the completed cm handle' + assert result.size() == 1 + assert result[0] == 'my completed cm handle' + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDogSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDogSpec.groovy index af546b7f5..b6259bdf3 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDogSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDogSpec.groovy @@ -20,7 +20,6 @@ package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability -import com.hazelcast.map.IMap import org.onap.cps.ncmp.api.impl.client.DmiRestClient import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel import spock.lang.Specification @@ -28,7 +27,7 @@ import spock.lang.Specification class DMiPluginWatchDogSpec extends Specification { - def mockTrustLevelPerDmiPlugin = Mock(IMap) + def mockTrustLevelPerDmiPlugin = Mock(Map) def mockDmiRestClient = Mock(DmiRestClient) def objectUnderTest = new DMiPluginWatchDog(mockTrustLevelPerDmiPlugin, mockDmiRestClient) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditionsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditionsSpec.groovy index f0e2d9f0b..100705ff5 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditionsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditionsSpec.groovy @@ -26,10 +26,11 @@ class CmHandleQueryConditionsSpec extends Specification { def 'CmHandle query condition names.'() { expect: '3 conditions with the correct names' - assert CmHandleQueryConditions.ALL_CONDITION_NAMES.size() == 3 + assert CmHandleQueryConditions.ALL_CONDITION_NAMES.size() == 4 assert CmHandleQueryConditions.ALL_CONDITION_NAMES.containsAll('hasAllProperties', 'hasAllModules', - 'cmHandleWithCpsPath') + 'cmHandleWithCpsPath', + 'cmHandleWithTrustLevel') } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy index 6c4575515..2b17e5d41 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy @@ -32,7 +32,7 @@ import spock.lang.Specification class DmiServiceUrlBuilderSpec extends Specification { static YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('dmiServiceName', - 'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')) + 'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'),'') NcmpConfiguration.DmiProperties dmiProperties = new NcmpConfiguration.DmiProperties() diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy index a58f22b95..ca0015e99 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy @@ -47,7 +47,7 @@ class YangModelCmHandleSpec extends Specification { .withOperationalDataStores(DataStoreSyncState.SYNCHRONIZED, 'some-sync-time').build() ncmpServiceCmHandle.setCompositeState(compositeState) when: 'it is converted to a yang model cm handle' - def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('', '', '', ncmpServiceCmHandle) + def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('', '', '', ncmpServiceCmHandle,'') then: 'the result has the right size' assert objectUnderTest.dmiProperties.size() == 1 and: 'the DMI property in the result has the correct name and value' @@ -63,7 +63,8 @@ class YangModelCmHandleSpec extends Specification { def 'Resolve DMI service name: #scenario and #requiredService service require.'() { given: 'a yang model cm handle' - def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName, dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')) + def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName, + dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'),'') expect: assert objectUnderTest.resolveDmiServiceName(requiredService) == expectedService where: diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy index ffdd67265..a3a5efc74 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy @@ -21,12 +21,15 @@ package org.onap.cps.ncmp.api.inventory +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel + import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS +import com.hazelcast.map.IMap import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueriesImpl import org.onap.cps.ncmp.api.impl.inventory.CmHandleState import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState @@ -37,8 +40,9 @@ import spock.lang.Specification class CmHandleQueriesImplSpec extends Specification { def cpsDataPersistenceService = Mock(CpsDataPersistenceService) + def trustLevelPerCmHandle = [ 'my completed cm handle': TrustLevel.COMPLETE, 'my untrusted cm handle': TrustLevel.NONE ] - def objectUnderTest = new CmHandleQueriesImpl(cpsDataPersistenceService) + def objectUnderTest = new CmHandleQueriesImpl(cpsDataPersistenceService, trustLevelPerCmHandle) @Shared def static sampleDataNodes = [new DataNode()] @@ -60,11 +64,21 @@ class CmHandleQueriesImplSpec extends Specification { result.containsAll(expectedCmHandleIds) result.size() == expectedCmHandleIds.size() where: 'the following data is used' - scenario | publicPropertyPairs || expectedCmHandleIds - 'single property matches' | ['Contact' : 'newemailforstore@bookstore.com'] || ['PNFDemo', 'PNFDemo2', 'PNFDemo4'] - 'public property does not match' | ['wont_match' : 'wont_match'] || [] - '2 properties, only one match' | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': 'newemailforstore2@bookstore.com'] || ['PNFDemo4'] - '2 properties, no matches' | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': ''] || [] + scenario | publicPropertyPairs || expectedCmHandleIds + 'single property matches' | [Contact: 'newemailforstore@bookstore.com'] || ['PNFDemo', 'PNFDemo2', 'PNFDemo4'] + 'public property does not match' | [wont_match: 'wont_match'] || [] + '2 properties, only one match' | [Contact: 'newemailforstore@bookstore.com', Contact2: 'newemailforstore2@bookstore.com'] || ['PNFDemo4'] + '2 properties, no matches' | [Contact: 'newemailforstore@bookstore.com', Contact2: ''] || [] + } + + def 'Query cm handles on trust level'() { + given: 'query properties for trustlevel COMPLETE' + def trustLevelPropertyQueryPairs = ['trustLevel' : TrustLevel.COMPLETE.toString()] + when: 'the query is executed' + def result = objectUnderTest.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs) + then: 'the result only contains the completed cm handle' + assert result.size() == 1 + assert result[0] == 'my completed cm handle' } def 'Query CmHandles using empty public properties query pair.'() { @@ -153,7 +167,7 @@ class CmHandleQueriesImplSpec extends Specification { cpsPath + '/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS) >> Arrays.asList(cmHandleDataNode) when: 'get cm handles by cps path is invoked' - def result = objectUnderTest.queryCmHandleDataNodesByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.queryCmHandleAncestorsByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS) then: 'the returned result is a list of data nodes returned by cps data service' assert result.contains(cmHandleDataNode) } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy index e96105559..b547da7a1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy @@ -20,17 +20,24 @@ package org.onap.cps.ncmp.api.inventory.sync +import org.onap.cps.spi.FetchDescendantsOption + import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE import org.onap.cps.api.CpsAdminService +import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsModuleService import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncService import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries +import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.spi.CascadeDeleteAllowed import org.onap.cps.spi.exceptions.SchemaSetNotFoundException import org.onap.cps.spi.model.ModuleReference +import org.onap.cps.utils.JsonObjectMapper import spock.lang.Specification class ModuleSyncServiceSpec extends Specification { @@ -38,17 +45,23 @@ class ModuleSyncServiceSpec extends Specification { def mockCpsModuleService = Mock(CpsModuleService) def mockDmiModelOperations = Mock(DmiModelOperations) def mockCpsAdminService = Mock(CpsAdminService) + def mockCmHandleQueries = Mock(CmHandleQueries) + def mockCpsDataService = Mock(CpsDataService) + def mockJsonObjectMapper = Mock(JsonObjectMapper) - def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, mockCpsAdminService) + def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, mockCpsAdminService, + mockCmHandleQueries, mockCpsDataService, mockJsonObjectMapper) def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME def 'Sync model for a (new) cm handle with #scenario'() { - given: 'a cm handle' + given: 'a cm handle having lock reason : MODULE_UPGRADE' def ncmpServiceCmHandle = new NcmpServiceCmHandle() + ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder() + .withLockReason(MODULE_UPGRADE, 'new moduleSetTag: someModuleSetTag').build()) def dmiServiceName = 'some service name' ncmpServiceCmHandle.cmHandleId = 'cmHandleId-1' - def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle) + def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'someModuleSetTag') and: 'DMI operations returns some module references' def moduleReferences = [ new ModuleReference('module1','1'), new ModuleReference('module2','2') ] mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences @@ -56,11 +69,14 @@ class ModuleSyncServiceSpec extends Specification { mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> toModuleReference(existingModuleResourcesInCps) and: 'DMI-Plugin returns resource(s) for "new" module(s)' mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('module1', '1')]) >> newModuleNameContentToMap + and: 'empty data node list is returned by cps path a query' + mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='someModuleSetTag']", + FetchDescendantsOption.OMIT_DESCENDANTS) >> Collections.emptyList() when: 'module sync is triggered' mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReference(identifiedNewModuleReferences) - objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle) + objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle) then: 'create schema set from module is invoked with correct parameters' - 1 * mockCpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', newModuleNameContentToMap, moduleReferences) + 1 * mockCpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', newModuleNameContentToMap, moduleReferences) and: 'anchor is created with the correct parameters' 1 * mockCpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', 'cmHandleId-1') where: 'the following parameters are used' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy index 231e34a92..a11f14838 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy @@ -69,8 +69,8 @@ class ModuleSyncTasksSpec extends Specification { 1 * mockModuleSyncService.deleteSchemaSetIfExists('cm-handle-1') 1 * mockModuleSyncService.deleteSchemaSetIfExists('cm-handle-2') and: 'module sync service is invoked for each cm handle' - 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } - 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-2') } + 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } + 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-2') } and: 'the state handler is called for the both cm handles' 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args -> assertBatch(args, ['cm-handle-1', 'cm-handle-2'], CmHandleState.READY) @@ -86,7 +86,7 @@ class ModuleSyncTasksSpec extends Specification { def cmHandleState = new CompositeState(cmHandleState: CmHandleState.ADVISED) 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> cmHandleState and: 'module sync service attempts to sync the cm handle and throws an exception' - 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } + 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } when: 'module sync is executed' objectUnderTest.performModuleSync([cmHandle], batchCount) then: 'update lock reason, details and attempts is invoked' @@ -112,7 +112,7 @@ class ModuleSyncTasksSpec extends Specification { moduleSyncStartedOnCmHandles.put('cm-handle-1', 'started') moduleSyncStartedOnCmHandles.put('cm-handle-2', 'started') and: 'sync utils retry locked cm handle returns #isReadyForRetry' - mockSyncUtils.needsModuleSyncRetry(lockedState) >>> isReadyForRetry + mockSyncUtils.needsModuleSyncRetryOrUpgrade(lockedState) >>> isReadyForRetry when: 'resetting failed cm handles' objectUnderTest.resetFailedCmHandles([yangModelCmHandle1, yangModelCmHandle2]) then: 'updated to state "ADVISED" from "READY" is called as often as there are cm handles ready for retry' @@ -135,7 +135,7 @@ class ModuleSyncTasksSpec extends Specification { when: 'module sync poll is executed' objectUnderTest.performModuleSync([cmHandle1], batchCount) then: 'module sync service is invoked for cm handle' - 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } + 1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') } and: 'the entry for other cm handle is still in the progress map' assert moduleSyncStartedOnCmHandles.get('other-cm-handle') != null } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy index d85686aa3..390e88b3d 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy @@ -111,7 +111,7 @@ class ModuleSyncWatchdogSpec extends Specification { def 'Reset failed cm handles.'() { given: 'sync utilities returns failed cm handles' def failedCmHandles = [new YangModelCmHandle()] - mockSyncUtils.getModuleSyncFailedCmHandles() >> failedCmHandles + mockSyncUtils.getCmHandlesThatFailedModelSyncOrUpgrade() >> failedCmHandles when: 'reset failed cm handles is started' objectUnderTest.resetPreviouslyFailedCmHandles() then: 'it is delegated to the module sync task (service)' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy index 8fdbb6f53..00d14cd2a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy @@ -115,11 +115,11 @@ class SyncUtilsSpec extends Specification{ def 'Get all locked Cm-Handle where Lock Reason is MODULE_SYNC_FAILED cm handle #scenario'() { given: 'the cps (persistence service) returns a collection of data nodes' - mockCmHandleQueries.queryCmHandleDataNodesByCpsPath( - '//lock-reason[@reason="MODULE_SYNC_FAILED"]', + mockCmHandleQueries.queryCmHandleAncestorsByCpsPath( + '//lock-reason[@reason="MODULE_SYNC_FAILED" or @reason="MODULE_UPGRADE"]', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode] when: 'get locked Misbehaving cm handle is called' - def result = objectUnderTest.getModuleSyncFailedCmHandles() + def result = objectUnderTest.getCmHandlesThatFailedModelSyncOrUpgrade() then: 'the returned cm handle collection is the correct size' result.size() == 1 and: 'the correct cm handle is returned' @@ -133,7 +133,7 @@ class SyncUtilsSpec extends Specification{ lastUpdatedTime = neverUpdatedBefore } when: 'checking to see if cm handle is ready for retry' - def result = objectUnderTest.needsModuleSyncRetry(new CompositeStateBuilder() + def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder() .withLockReason(MODULE_SYNC_FAILED, lockDetails) .withLastUpdatedTime(lastUpdatedTime).build()) then: 'retry is only attempted when expected' @@ -151,16 +151,18 @@ class SyncUtilsSpec extends Specification{ def 'Retry Locked Cm-Handle with other lock reasons (category) #lockReasonCategory'() { when: 'checking to see if cm handle is ready for retry' - def result = objectUnderTest.needsModuleSyncRetry(new CompositeStateBuilder() + def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder() .withLockReason(lockReasonCategory, 'some details') .withLastUpdatedTime(nowAsString).build()) - then: 'retry attempt is never triggered' - assert result == false + then: 'verify retry attempts' + assert result == retryAttempt and: 'logs contain related information' - def logs = loggingListAppender.list.toString() - assert logs.contains('Locked for other reason') + def logs = loggingListAppender.list.toString() + assert logs.contains(logReason) where: 'the following lock reasons occurred' - lockReasonCategory << [MODULE_UPGRADE, MODULE_UPGRADE_FAILED] + scenario | lockReasonCategory || logReason | retryAttempt + 'module upgrade' | MODULE_UPGRADE || 'Locked for module upgrade.' | true + 'module sync failed' | MODULE_SYNC_FAILED || 'First Attempt:' | false } def 'Get a Cm-Handle where #scenario'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy index dba29343e..d76f91223 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022 Bell Canada + * Modifications Copyright (C) 2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +21,11 @@ package org.onap.cps.ncmp.api.models -import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR + import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status import spock.lang.Specification - import java.util.stream.Collectors class CmHandleRegistrationResponseSpec extends Specification { @@ -37,7 +39,7 @@ class CmHandleRegistrationResponseSpec extends Specification { assert it.status == Status.SUCCESS } and: 'error details are null' - cmHandleRegistrationResponse.registrationError == null + cmHandleRegistrationResponse.ncmpResponseStatus == null cmHandleRegistrationResponse.errorText == null } @@ -47,33 +49,29 @@ class CmHandleRegistrationResponseSpec extends Specification { CmHandleRegistrationResponse.createFailureResponse('cmHandle', new Exception('unexpected error')) then: 'the response is created with expected value' with(cmHandleRegistrationResponse) { - assert it.registrationError == RegistrationError.UNKNOWN_ERROR + assert it.ncmpResponseStatus == UNKNOWN_ERROR assert it.cmHandle == 'cmHandle' assert errorText == 'unexpected error' } } - def 'Failed cm-handle Registration Response: for #scenario'() { - when: 'cm-handle failure response is created for #scenario' - def cmHandleRegistrationResponse = - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, registrationError) + def 'Failed cm-handle Registration Response'() { + when: 'cm-handle failure response is created' + def cmHandleRegistrationResponse = + CmHandleRegistrationResponse.createFailureResponse('cmHandle', CM_HANDLE_ALREADY_EXIST) then: 'the response is created with expected value' - with(cmHandleRegistrationResponse) { - assert it.registrationError == registrationError - assert it.cmHandle == cmHandleId - assert it.status == Status.FAILURE - assert errorText == registrationError.errorText - } - where: - scenario | cmHandleId | registrationError - 'cm-handle already exists' | 'cmHandle' | RegistrationError.CM_HANDLE_ALREADY_EXIST - 'cm-handle id is invalid' | 'cm handle' | RegistrationError.CM_HANDLE_INVALID_ID + with(cmHandleRegistrationResponse) { + assert it.ncmpResponseStatus == CM_HANDLE_ALREADY_EXIST + assert it.cmHandle == 'cmHandle' + assert it.status == Status.FAILURE + assert errorText == CM_HANDLE_ALREADY_EXIST.message + } } def 'Failed cm-handle Registration with multiple responses.'() { when: 'cm-handle failure response is created for 2 xpaths' def cmHandleRegistrationResponses = - CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","somePathWithId[@id='456']"], RegistrationError.CM_HANDLE_ALREADY_EXIST) + CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","somePathWithId[@id='456']"], CM_HANDLE_ALREADY_EXIST) then: 'the response has the correct cm handle ids' assert cmHandleRegistrationResponses.size() == 2 assert cmHandleRegistrationResponses.stream().map(it -> it.cmHandle).collect(Collectors.toList()) @@ -83,12 +81,9 @@ class CmHandleRegistrationResponseSpec extends Specification { def 'Failed cm-handle Registration with multiple responses with an unexpected xpath.'() { when: 'cm-handle failure response is created for one valid and one unexpected xpath' def cmHandleRegistrationResponses = - CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","valid/xpath/without-id[@key='123']"], RegistrationError.CM_HANDLE_ALREADY_EXIST) + CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","valid/xpath/without-id[@key='123']"], CM_HANDLE_ALREADY_EXIST) then: 'the response has only one entry' assert cmHandleRegistrationResponses.size() == 1 } - - - } diff --git a/cps-parent/pom.xml b/cps-parent/pom.xml index 41215567c..37c9ad3f7 100755 --- a/cps-parent/pom.xml +++ b/cps-parent/pom.xml @@ -32,7 +32,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT pom diff --git a/cps-path-parser/pom.xml b/cps-path-parser/pom.xml index 8ce3fa5b2..4fc7d405d 100644 --- a/cps-path-parser/pom.xml +++ b/cps-path-parser/pom.xml @@ -23,7 +23,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml diff --git a/cps-rest/pom.xml b/cps-rest/pom.xml index cd5bc0d36..f99e48f11 100755 --- a/cps-rest/pom.xml +++ b/cps-rest/pom.xml @@ -28,7 +28,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml @@ -39,6 +39,7 @@ ${project.groupId} cps-service + org.springframework.boot spring-boot-starter-web @@ -65,6 +66,15 @@ org.springframework spring-aspects + + + jakarta.validation + jakarta.validation-api + + + io.gsonfire + gson-fire + io.swagger.core.v3 swagger-annotations @@ -122,14 +132,6 @@ - - jakarta.validation - jakarta.validation-api - - - io.gsonfire - gson-fire - diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml index f8fd730a1..63a6ab21c 100644 --- a/cps-ri/pom.xml +++ b/cps-ri/pom.xml @@ -26,7 +26,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml 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 ca88a4da2..0d77530b3 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 @@ -46,7 +46,6 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; -import org.onap.cps.spi.CpsAdminPersistenceService; import org.onap.cps.spi.CpsModulePersistenceService; import org.onap.cps.spi.entities.DataspaceEntity; import org.onap.cps.spi.entities.SchemaSetEntity; @@ -89,8 +88,6 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ private final DataspaceRepository dataspaceRepository; - private final CpsAdminPersistenceService cpsAdminPersistenceService; - private final ModuleReferenceRepository moduleReferenceRepository; @Override diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java index b8503a7fe..19646c523 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java @@ -70,13 +70,25 @@ public interface AnchorRepository extends JpaRepository { Integer countByDataspace(DataspaceEntity dataspaceEntity); - @Query(value = "SELECT anchor.* FROM yang_resource\n" - + "JOIN schema_set_yang_resources ON schema_set_yang_resources.yang_resource_id = yang_resource.id\n" - + "JOIN schema_set ON schema_set.id = schema_set_yang_resources.schema_set_id\n" - + "JOIN anchor ON anchor.schema_set_id = schema_set.id\n" - + "WHERE schema_set.dataspace_id = :dataspaceId AND module_name = ANY (:moduleNames)\n" - + "GROUP BY anchor.id, anchor.name, anchor.dataspace_id, anchor.schema_set_id\n" - + "HAVING COUNT(DISTINCT module_name) = :sizeOfModuleNames", nativeQuery = true) + @Query(value = """ + SELECT + anchor.* + FROM + yang_resource + JOIN schema_set_yang_resources ON schema_set_yang_resources.yang_resource_id = yang_resource.id + JOIN schema_set ON schema_set.id = schema_set_yang_resources.schema_set_id + JOIN anchor ON anchor.schema_set_id = schema_set.id + WHERE + schema_set.dataspace_id = :dataspaceId + AND module_name = ANY ( :moduleNames ) + GROUP BY + anchor.id, + anchor.name, + anchor.dataspace_id, + anchor.schema_set_id + HAVING + COUNT(DISTINCT module_name) = :sizeOfModuleNames + """, nativeQuery = true) Collection getAnchorsByDataspaceIdAndModuleNames(@Param("dataspaceId") int dataspaceId, @Param("moduleNames") String[] moduleNames, @Param("sizeOfModuleNames") int sizeOfModuleNames); 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 df21ccc52..e83379633 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 @@ -41,56 +41,65 @@ public interface YangResourceRepository extends JpaRepository findAllModuleReferencesByDataspace(@Param("dataspaceName") String dataspaceName); - @Query(value = "SELECT DISTINCT\n" - + "yang_resource.module_Name AS module_name,\n" - + "yang_resource.revision AS revision\n" - + "FROM\n" - + "dataspace\n" - + "JOIN anchor ON anchor.dataspace_id = dataspace.id\n" - + "JOIN schema_set ON schema_set.id = anchor.schema_set_id\n" - + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = " - + "schema_set.id\n" - + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n" - + "WHERE\n" - + "dataspace.name = :dataspaceName AND\n" - + "anchor.name =:anchorName", nativeQuery = true) + @Query(value = """ + SELECT DISTINCT + yang_resource.module_name AS module_name, + yang_resource.revision AS revision + FROM + dataspace + JOIN anchor ON anchor.dataspace_id = dataspace.id + JOIN schema_set ON schema_set.id = anchor.schema_set_id + JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = schema_set.id + JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id + WHERE + dataspace.name = :dataspaceName + AND anchor.name = :anchorName + """, nativeQuery = true) Set findAllModuleReferencesByDataspaceAndAnchor( @Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName); - @Query(value = "SELECT DISTINCT yang_resource.*\n" - + "FROM dataspace\n" - + "JOIN anchor ON anchor.dataspace_id = dataspace.id\n" - + "JOIN schema_set ON schema_set.id = anchor.schema_set_id\n" - + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = schema_set.id\n" - + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n" - + "WHERE dataspace.name = :dataspaceName " - + "AND anchor.name =:anchorName", nativeQuery = true) + @Query(value = """ + SELECT DISTINCT + yang_resource.* + FROM + dataspace + JOIN anchor ON anchor.dataspace_id = dataspace.id + JOIN schema_set ON schema_set.id = anchor.schema_set_id + JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = schema_set.id + JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id + WHERE + dataspace.name = :dataspaceName + AND anchor.name = :anchorName + """, nativeQuery = true) Set findAllModuleDefinitionsByDataspaceAndAnchor( @Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName); - @Query(value = "SELECT DISTINCT\n" - + "yang_resource.*\n" - + "FROM\n" - + "dataspace\n" - + "JOIN schema_set ON schema_set.dataspace_id = dataspace.id\n" - + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = " - + "schema_set.id\n" - + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n" - + "WHERE\n" - + "dataspace.name = :dataspaceName and yang_resource.module_Name = ANY (:moduleNames)", nativeQuery = true) + @Query(value = """ + SELECT DISTINCT + yang_resource.* + FROM + dataspace + JOIN schema_set ON schema_set.dataspace_id = dataspace.id + JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = schema_set.id + JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id + WHERE + dataspace.name = :dataspaceName + AND yang_resource.module_name = ANY ( :moduleNames ) + """, nativeQuery = true) Set findAllModuleReferencesByDataspaceAndModuleNames( @Param("dataspaceName") String dataspaceName, @Param("moduleNames") String[] moduleNames); 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 52651c6b1..9696b28cd 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 @@ -69,7 +69,7 @@ class CpsModulePersistenceServiceSpec extends Specification { def setup() { objectUnderTest = new CpsModulePersistenceServiceImpl(yangResourceRepositoryMock, schemaSetRepositoryMock, - dataspaceRepositoryMock, cpsAdminPersistenceServiceMock, moduleReferenceRepositoryMock) + dataspaceRepositoryMock, moduleReferenceRepositoryMock) } def 'Store schema set error scenario: #scenario.'() { diff --git a/cps-service/pom.xml b/cps-service/pom.xml index 4a3c90e41..0787409ca 100644 --- a/cps-service/pom.xml +++ b/cps-service/pom.xml @@ -29,13 +29,33 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml cps-service + + org.springframework + spring-messaging + + + org.springframework.boot + spring-boot-starter-cache + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.kafka + spring-kafka + com.github.ben-manes.caffeine caffeine @@ -54,6 +74,10 @@ io.micrometer micrometer-core + + jakarta.validation + jakarta.validation-api + net.logstash.logback logstash-logback-encoder @@ -104,33 +128,12 @@ org.slf4j slf4j-api - - org.springframework - spring-messaging - - - org.springframework.boot - spring-boot-starter-cache - 3.0.0 - - - org.springframework.boot - spring-boot-starter-aop - - - org.springframework.boot - spring-boot-starter-validation - - - org.springframework.kafka - spring-kafka - + org.springframework spring-test test - org.codehaus.groovy groovy @@ -176,9 +179,5 @@ aspectjrt test - - jakarta.validation - jakarta.validation-api - 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 5ff08c9ac..e8c3e7775 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 @@ -47,13 +47,13 @@ public interface CpsModuleService { Map yangResourcesNameToContentMap); /** - * Create a schema set from new modules and existing modules. + * Create or upgrade a schema set from new modules and existing modules or only existing modules. * @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 allModuleReferences All YANG resource module references */ - void createSchemaSetFromModules(String dataspaceName, String schemaSetName, + void createOrUpgradeSchemaSetFromModules(String dataspaceName, String schemaSetName, Map newModuleNameToContentMap, Collection allModuleReferences); diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java index 7db87e87e..1d68450f8 100755 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java @@ -31,6 +31,7 @@ import static org.onap.cps.notification.Operation.UPDATE; import io.micrometer.core.annotation.Timed; import java.io.Serializable; import java.time.OffsetDateTime; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -321,28 +322,12 @@ public class CpsDataServiceImpl implements CpsDataService { processDataUpdatedEventAsync(anchor, listNodeXpath, DELETE, observedTimestamp); } - private DataNode buildDataNode(final Anchor anchor, final String parentNodeXpath, final String nodeData, - final ContentType contentType) { - final SchemaContext schemaContext = getSchemaContext(anchor); - - if (ROOT_NODE_XPATH.equals(parentNodeXpath)) { - final ContainerNode containerNode = timedYangParser.parseData(contentType, nodeData, schemaContext); - return new DataNodeBuilder().withContainerNode(containerNode).build(); - } - - final ContainerNode containerNode = - timedYangParser.parseData(contentType, nodeData, schemaContext, parentNodeXpath); - - return new DataNodeBuilder() - .withParentNodeXpath(parentNodeXpath) - .withContainerNode(containerNode) - .build(); - } - private Collection buildDataNodes(final Anchor anchor, final Map nodesJsonData) { - return nodesJsonData.entrySet().stream().map(nodeJsonData -> - buildDataNode(anchor, nodeJsonData.getKey(), - nodeJsonData.getValue(), ContentType.JSON)).collect(Collectors.toList()); + final Collection dataNodes = new ArrayList<>(); + for (final Map.Entry nodeJsonData : nodesJsonData.entrySet()) { + dataNodes.addAll(buildDataNodes(anchor, nodeJsonData.getKey(), nodeJsonData.getValue(), ContentType.JSON)); + } + return dataNodes; } private Collection buildDataNodes(final Anchor anchor, final String parentNodeXpath, 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 7d9c472e6..444c895fb 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 @@ -66,7 +66,7 @@ public class CpsModuleServiceImpl implements CpsModuleService { } @Override - public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName, + public void createOrUpgradeSchemaSetFromModules(final String dataspaceName, final String schemaSetName, final Map newModuleNameToContentMap, final Collection allModuleReferences) { cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy index b4ac7a68f..e1d15d68a 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy @@ -307,15 +307,16 @@ class CpsDataServiceImplSpec extends Specification { objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp) then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, - { dataNode -> dataNode.xpath[0] == expectedNodeXpath }) + { dataNode -> dataNode.xpath == expectedNodeXpath}) and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'following parameters were used' - scenario | parentNodeXpath | jsonData || expectedNodeXpath - 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree' - 'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']' + scenario | parentNodeXpath | jsonData || expectedNodeXpath + 'top level node' | '/' | '{"test-tree": {"branch": []}}' || ['/test-tree'] + 'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || ['/test-tree/branch[@name=\'Name\']'] + 'json list' | '/test-tree' | '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}' || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] } def 'Replace data node using multiple data nodes: #scenario.'() { @@ -327,14 +328,16 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, { dataNode -> dataNode.xpath == expectedNodeXpath}) and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(anchor, nodesJsonData.keySet()[0], Operation.UPDATE, observedTimestamp) - 1 * mockNotificationService.processDataUpdatedEvent(anchor, nodesJsonData.keySet()[1], Operation.UPDATE, observedTimestamp) + nodesJsonData.keySet().each { + 1 * mockNotificationService.processDataUpdatedEvent(anchor, it, Operation.UPDATE, observedTimestamp) + } and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'following parameters were used' scenario | nodesJsonData || expectedNodeXpath 'top level node' | ['/' : '{"test-tree": {"branch": []}}', '/test-tree' : '{"branch": [{"name":"Name"}]}'] || ["/test-tree", "/test-tree/branch[@name='Name']"] 'level 2 node' | ['/test-tree' : '{"branch": [{"name":"Name"}]}', '/test-tree/branch[@name=\'Name\']':'{"nest":{"name":"nestName"}}'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"] + 'json list' | ['/test-tree' : '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}'] || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] } def 'Replace data node with concurrency exception in persistence layer.'() { 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 a794c58fc..61f67416d 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 @@ -65,7 +65,7 @@ class CpsModuleServiceImplSpec extends Specification { def moduleReferenceForExistingModule = new ModuleReference('test', '2021-10-12','test.org') def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule] when: 'create schema set from modules method is invoked' - objectUnderTest.createSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference) + objectUnderTest.createOrUpgradeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference) then: 'processing is delegated to persistence service' 1 * mockCpsModulePersistenceService.storeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference) and: 'the CpsValidator is called on the dataspaceName and schemaSetName' diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml index 583268a50..97f1bdc03 100644 --- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml +++ b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml @@ -22,7 +22,7 @@ org.onap.cps dmi-plugin-demo-and-csit-stub - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT dmi-plugin-demo-and-csit-stub-app @@ -106,7 +106,7 @@ org.eclipse.jetty jetty-server - 11.0.14 + 11.0.16 jakarta.servlet diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml index 8daddba3b..3a46e4391 100644 --- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml +++ b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml @@ -21,7 +21,7 @@ org.onap.cps dmi-plugin-demo-and-csit-stub - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT dmi-plugin-demo-and-csit-stub-service diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java index 987436feb..3f9731d72 100644 --- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java +++ b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java @@ -20,6 +20,8 @@ package org.onap.cps.ncmp.dmi.rest.stub.controller; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESS; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.cloudevents.CloudEvent; @@ -28,12 +30,10 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; -import org.onap.cps.ncmp.api.NcmpEventResponseCode; import org.onap.cps.ncmp.api.impl.utils.EventDateTimeFormatter; import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.CmHandle; import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DataOperationRequest; @@ -161,9 +161,9 @@ public class DmiRestStubController { private DataOperationEvent getDataOperationEvent(final DataOperationRequest dataOperationRequest) { final Response response = new Response(); response.setOperationId(dataOperationRequest.getOperationId()); - response.setStatusCode(NcmpEventResponseCode.SUCCESS.getStatusCode()); - response.setStatusMessage(NcmpEventResponseCode.SUCCESS.getStatusMessage()); - response.setIds(dataOperationRequest.getCmHandles().stream().map(CmHandle::getId).collect(Collectors.toList())); + response.setStatusCode(SUCCESS.getCode()); + response.setStatusMessage(SUCCESS.getMessage()); + response.setIds(dataOperationRequest.getCmHandles().stream().map(CmHandle::getId).toList()); response.setResourceIdentifier(dataOperationRequest.getResourceIdentifier()); response.setOptions(dataOperationRequest.getOptions()); final String ietfNetworkTopologySample = ResourceFileReaderUtil @@ -176,7 +176,7 @@ public class DmiRestStubController { } catch (final ParseException parseException) { log.error("Unable to parse event result as json object. cause : {}", parseException.getMessage()); } - final List responseList = new ArrayList<>(); + final List responseList = new ArrayList<>(1); responseList.add(response); final Data data = new Data(); data.setResponses(responseList); diff --git a/dmi-plugin-demo-and-csit-stub/pom.xml b/dmi-plugin-demo-and-csit-stub/pom.xml index 430476985..88f11c797 100644 --- a/dmi-plugin-demo-and-csit-stub/pom.xml +++ b/dmi-plugin-demo-and-csit-stub/pom.xml @@ -22,7 +22,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index bb286fde2..2607385de 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -38,6 +38,7 @@ services: ports: - ${CPS_CORE_PORT:-8883}:8080 - ${CPS_CORE_MANAGEMENT_PORT:-8887}:8081 + - ${CPS_CORE_DEBUG_PORT:-5005}:5005 environment: CPS_USERNAME: ${CPS_CORE_USERNAME:-cpsuser} CPS_PASSWORD: ${CPS_CORE_PASSWORD:-cpsr0cks!} @@ -52,6 +53,7 @@ services: NOTIFICATION_DATASPACE_FILTER_PATTERNS: '.*' NCMP_TIMERS_ADVISED-MODULES-SYNC_SLEEP-TIME-MS: ${ADVISED_MODULES_SYNC_SLEEP_TIME_MS:-30000} NCMP_TIMERS_CM-HANDLE-DATA-SYNC_SLEEP-TIME-MS: ${CMHANDLE_DATA_SYNC_SLEEP_TIME_MS:-30000} + JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 restart: unless-stopped depends_on: - dbpostgresql diff --git a/docs/cps-ncmp-message-status-codes.rst b/docs/cps-ncmp-message-status-codes.rst index 1b74153d7..20a5ae3c1 100644 --- a/docs/cps-ncmp-message-status-codes.rst +++ b/docs/cps-ncmp-message-status-codes.rst @@ -10,15 +10,15 @@ CPS-NCMP Message Status Codes ############################# +-----------------+------------------------------------------------------+-----------------------------------+ - | Status Code | Status Message | Feature | + | Status Code | Status Message | Feature(s) | +=================+======================================================+===================================+ | 0 | Successfully applied changes | Data Operation | +-----------------+------------------------------------------------------+-----------------------------------+ | 1 | successfully applied subscription | CM Data Notification Subscription | +-----------------+------------------------------------------------------+-----------------------------------+ - | 100 | cm handle id(s) is(are) not found | Data Operation | + | 100 | cm handle id(s) is(are) not found | Data Operation, Inventory | +-----------------+------------------------------------------------------+-----------------------------------+ - | 101 | cm handle id(s) is(are) in non ready state | Data Operation | + | 101 | cm handle(s) not ready | Data Operation | +-----------------+------------------------------------------------------+-----------------------------------+ | 102 | dmi plugin service is not responding | Data Operation | +-----------------+------------------------------------------------------+-----------------------------------+ @@ -32,6 +32,12 @@ CPS-NCMP Message Status Codes +-----------------+------------------------------------------------------+-----------------------------------+ | 107 | southbound system is busy | Data Operation | +-----------------+------------------------------------------------------+-----------------------------------+ + | 108 | Unknown error | Inventory | + +-----------------+------------------------------------------------------+-----------------------------------+ + | 109 | cm-handle already exists | Inventory | + +-----------------+------------------------------------------------------+-----------------------------------+ + | 110 | cm-handle has an invalid character(s) in id | Inventory | + +-----------------+------------------------------------------------------+-----------------------------------+ .. note:: diff --git a/docs/cps-path.rst b/docs/cps-path.rst index 661178954..eb203d891 100644 --- a/docs/cps-path.rst +++ b/docs/cps-path.rst @@ -247,7 +247,7 @@ leaf-conditions - Using comparative operators with string values will lead to an error at runtime. This error can't be validated earlier as the datatype is unknown until the execution phase. - The key should be supplied with correct data type for it to be queried from DB. In the last example above the attribute code is of type Integer so the cps query will not work if the value is passed as string. - eg: ``//categories[@code="1"]`` or ``//categories[@code='1']`` will not work because the key attribute code is treated a string. + e.g.: ``//categories[@code="1"]`` or ``//categories[@code='1']`` will not work because the key attribute code is treated a string. **Notes** - For performance reasons it does not make sense to query using key leaf as attribute. If the key value is known it is better to execute a get request with the complete xpath. @@ -260,7 +260,7 @@ The text()-condition can be added to any CPS path query. **Syntax**: `` ( '/' '[text()=' ']' )?`` - ``cps-path``: Any CPS path query. - ``leaf-name``: The name of the leaf or leaf-list which value needs to be compared. - - ``string-value``: The required value of the leaf or leaf-list element as a string wrapped in quotation marks (U+0022) or apostrophes (U+0027). This wil still match integer values. + - ``string-value``: The required value of the leaf or leaf-list element as a string wrapped in quotation marks (U+0022) or apostrophes (U+0027). This will still match integer values. **Examples** - ``//book/label[text()="classic"]`` diff --git a/docs/cps-stubs.rst b/docs/cps-stubs.rst new file mode 100644 index 000000000..00577eb0d --- /dev/null +++ b/docs/cps-stubs.rst @@ -0,0 +1,120 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +.. Copyright (C) 2023 Nordix Foundation + +.. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING + +.. _cpsStubs: + + +CPS Stubs +######### + +.. toctree:: + :maxdepth: 1 + +NCMP Stubs +========== + +The CPS NCMP stub module provides the capability to create dynamic and customizable stubs, offering control over the responses generated for each endpoint. This capability ensures that client interactions adhere to a specified NCMP interface, enabling comprehensive testing and validation of your applications. + +The NCMP stub RestController is an extended implementation of the actual NCMP interface. It can be deployed as part of the application JAR or within a SpringBootTest JUnit environment, allowing you to define dynamic responses for each endpoint and allowing testing against real stub interfaces. + +Prerequisites +============= + +Ensure you meet the following prerequisites: + +1. **Required Java Installation:** + + Ensure that you have the required Java installed on your system. + +2. **Access to Gerrit and Maven Installation (for building CPS project locally):** + + - Ensure you have access to the ONAP Gerrit repository. + + - If you plan to build the CPS project locally, make sure you have Maven installed. + +Method 1: Running Stubs as an Application +========================================= + +Follow these steps to run the CPS-NCMP stub application: + +1. **Download Application Jar:** + + You can obtain the CPS-NCMP stub application jar in one of the following ways: + + - **Option 1: Download from Nexus Repository:** + + Download the application jar from the Nexus repository at `https://nexus.onap.org/content/repositories/releases/org/onap/cps/cps-ncmp-rest-stub-app/`_. + + - **Option 2: Build Locally:** + + To build the CPS project locally, navigate to the project's root directory. Once there, you can build the project using :code:`mvn clean install`, and the application CPS-NCMP stub application jar can be found in the following location: + + :: + + cps/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/target/ + +2. **Run the Application:** + + After obtaining the application jar, use the following command to run it: + + .. code-block:: bash + + java -jar ./cps-ncmp-rest-stub-app-.jar + + Replace ```` with the actual version number of the application jar. + +This will start the CPS-NCMP stub application, and you can interact with it as needed. + +.. _`https://nexus.onap.org/content/repositories/releases/org/onap/cps/cps-ncmp-rest-stub-app/`: https://nexus.onap.org/content/repositories/releases/org/onap/cps/cps-ncmp-rest-stub-app/ + +Method 2: Using Stubs in Unit Tests +=================================== +1. **Add Dependency to pom.xml:** + + To include the required module in your project, add the following dependency to your `pom.xml` file: + + .. code-block:: xml + + + org.onap.cps + cps-ncmp-rest-stub-service + VERSION + + + Replace ``VERSION`` with the actual version number. + +2. **Using Custom Response Objects:** + + If you prefer to use custom response objects instead of the built-in ones, follow these steps: + + Modify the `application.yaml` file located in your project's test resources directory (`src/test/resources`). + + Add the following property to the `application.yaml` file, specifying the directory that contains your custom response objects: + + .. code-block:: yaml + + stub: + path: "/my_custom_stubs/" + + **Note:** Custom response objects can be placed in the `src/test/resources` directory of your project under the directory defined in above property. Refer to the `examples `_ included in the CPS source repository for reference. + +3. **Simple Test Code:** + + You may refer to the sample test code 'SampleCpsNcmpClientSpec.groovy' in the local CPS project under the following directory: + + :: + + /cps/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/ + + Alternatively, you can refer to the `example `_ included in the CPS source repository. + +**Custom Responses for Supported Endpoints** + + Only the following endpoints are supported for the first draft. To use your custom response objects for these endpoints, create the corresponding JSON files: + + - For RequestMethod.GET /v1/ch/{cm-handle}/data/ds/{datastore-name}, create "passthrough-operational-example.json". + + - For RequestMethod.POST /v1/ch/searches, create "cmHandlesSearch.json". diff --git a/docs/deployment.rst b/docs/deployment.rst index 0642e6a8e..0fee55969 100644 --- a/docs/deployment.rst +++ b/docs/deployment.rst @@ -197,8 +197,8 @@ Any spring supported property can be configured by providing in ``config.additio | logging.level | Logging level set in cps-core | info | | | | | +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+ -| config.useStrimziKafka | If targeting a custom kafka cluster, ie useStrimziKafka: false, the config.eventPublisher.spring.kafka | true | -| | values below must be set. | | +| config.useStrimziKafka | If targeting a custom kafka cluster, i.e. useStrimziKafka: false, the | true | +| | config.eventPublisher.spring.kafka values below must be set. | | +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+ | config.eventPublisher. | Kafka hostname and port | ``:9092`` | | spring.kafka.bootstrap-servers | | | diff --git a/docs/design.rst b/docs/design.rst index c6413c294..80eb5f152 100755 --- a/docs/design.rst +++ b/docs/design.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-2022 Nordix Foundation +.. Copyright (C) 2021-2023 Nordix Foundation .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING .. _design: @@ -83,3 +83,34 @@ NCMP Inventory CM Handle Querying The CM Handle searches ncmp inventory endpoints can be used to query for CM Handles or CM Handle IDs. This endpoint is described in detail in :doc:`ncmp-inventory-querying`. + +Common NCMP Response Codes +========================== + +NCMP uses common responses codes in REST responses and events. Also the DMI plugin interface uses these codes which are defined here: + +.. toctree:: + :maxdepth: 1 + + cps-ncmp-message-status-codes.rst + +Contract Testing (stubs) +======================== + +The CPS team is committed to supporting consumers of our APIs through contract testing. +Obviously we test our own contracts on a continuous basis as part of the build and delivery process. +CPS uses a contract-first approach. That means we write our OpenAPi contracts first and then generate the interface code from that. +This means our interface implementation simply cannot deviate from the OpenApi contracts we deliver. + +Another advantage is that we can also generate 'stubs'. Stubs are a basic implementation of the same interface for testing purposes. +These stubs can be used by clients for unit testing but also for more higher level integration-like testing where the real service is replaced by a stub. +This can be useful for faster feedback loops where deployment of a full stack is difficult and not strictly needed for the purpose of the tests. + +Stubs for contract testing typically always return the same response which is sufficient for the strict definition of a contract test. +However it is often useful to allow more variation in the responses so different clients or the same client can test different scenarios without having to mock the service. +CPS has implemented what we call 'extended stubs' that allow clients to provide alternate responses.implementation + +The available stubs and how to use them are described in :doc:'cps-stubs'. + + + diff --git a/docs/index.rst b/docs/index.rst index c1427ad38..573bd5412 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,6 +25,7 @@ CPS cps-events.rst deployment.rst release-notes.rst + cps-stubs.rst DMI-Plugin ========== diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 26a7a552e..e6b81fcba 100755 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -16,6 +16,33 @@ CPS Release Notes .. * * * MONTREAL * * * .. ======================== +Version: 3.3.9 +============== + +Release Data +------------ + ++--------------------------------------+--------------------------------------------------------+ +| **CPS Project** | | +| | | ++--------------------------------------+--------------------------------------------------------+ +| **Docker images** | onap/cps-and-ncmp:3.3.9 | +| | | ++--------------------------------------+--------------------------------------------------------+ +| **Release designation** | 3.3.9 Montreal | +| | | ++--------------------------------------+--------------------------------------------------------+ +| **Release date** | Not yet released | +| | | ++--------------------------------------+--------------------------------------------------------+ + +Bug Fixes +--------- +3.3.9 + +Features +-------- + Version: 3.3.8 ============== @@ -32,7 +59,7 @@ Release Data | **Release designation** | 3.3.8 Montreal | | | | +--------------------------------------+--------------------------------------------------------+ -| **Release date** | Not yet released | +| **Release date** | 2023 September 29 | | | | +--------------------------------------+--------------------------------------------------------+ @@ -42,6 +69,7 @@ Bug Fixes Features -------- + - `CPS-1888 `_ Uplift Spring Boot to 3.1.2. Version: 3.3.7 ============== @@ -70,7 +98,7 @@ Bug Fixes Features -------- -- `CPS-1789 `_ CPS Upgrade to Springboot 3.0. + - `CPS-1789 `_ CPS Upgrade to Springboot 3.0. Note ---- @@ -487,7 +515,7 @@ Bug Fixes - `CPS-1289 `_ Getting wrong error code for create node api - `CPS-1326 `_ Creation of DataNodeBuilder with module name prefix is very slow - `CPS-1344 `_ Top level container (prefix) is not always the first module - - `CPS-1350 `_ Add Basic Auth to CPS/NCMP OpenAPI Definitions. + - `CPS-1350 `_ Add Basic Authentication to CPS/NCMP OpenAPI Definitions. - `CPS-1352 `_ Handle YangChoiceNode in right format. - `CPS-1409 `_ Fix Delete uses case with '/' in path. - `CPS-1433 `_ Fix to allow posting data with '/' key fields. @@ -673,7 +701,7 @@ Bug Fixes - `CPS-957 `_ NCMP: fix getResourceDataForPassthroughOperational endpoint - `CPS-1020 `_ DuplicatedYangResourceException error at parallel cmHandle registration - `CPS-1056 `_ Wrong error response format in case of Dmi plugin error - - `CPS-1067 `_ NCMP returns 500 error on searches endpoint when No DMi Handles registered + - `CPS-1067 `_ NCMP returns 500 error on searches endpoint when No DMI Handles registered - `CPS-1085 `_ Performance degradation on ncmp/v1/ch/searches endpoint - `CPS-1088 `_ Kafka consumer can not be turned off - `CPS-1097 `_ Unable to change state from LOCKED to ADVISED @@ -785,7 +813,7 @@ Bug Fixes - `CPS-886 `_ Fragment handling decreasing performance for large number of cmHandles - `CPS-887 `_ Increase performance of cmHandle registration for large number of schema sets in DB - `CPS-892 `_ Fixed the response code during CM-Handle Registration from 201 CREATED to 204 NO_CONTENT - - `CPS-893 `_ NCMP Java API depends on NCM-Rest-API (cyclic) through json properties on Java API + - `CPS-893 `_ NCMP Java API depends on NCMP-Rest-API (cyclic) through json properties on Java API Known Limitations, Issues and Workarounds ----------------------------------------- @@ -1240,7 +1268,7 @@ Security Notes *Known Security Issues* - * Weak Crytography using md5 + * Weak Cryptography using md5 * Risk seen in Zip file expansion *Known Vulnerabilities in Used Modules* diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt new file mode 100644 index 000000000..3bfa9a0a3 --- /dev/null +++ b/docs/spelling_wordlist.txt @@ -0,0 +1,67 @@ +api +async +authorization +boolean +behavior +camelCasing +cmHandle +cmHandles +config +color +cps +cpsPath +cpsPaths +csit +customizations +dataschema +dataspace +dataspaces +datasource +datastore +datastores +deliverables +dmi +dmiPlugin +dmiPlugins +rnv +hazelcast +hostname +jira +json +kafka +kibana +kubernetes +kohn +lifecycle +liquibase +logback +modeled +modeling +normalized +ncmp +onap +openapi +optimized +passthrough +performant +postgres +queryable +repo +runtime +schemas +sql +ssl +standardized +synchronize +synchronization +undeploying +utilizes +xml +xNF +xNFs +xpath +xpaths +xPath +XPath +XPaths +yaml \ No newline at end of file diff --git a/docs/tox.ini b/docs/tox.ini index 8684276f9..7020752c8 100644 --- a/docs/tox.ini +++ b/docs/tox.ini @@ -21,23 +21,23 @@ minversion = 1.6 envlist = docs,docs-linkcheck,docs-spellcheck skipsdist = true [testenv:docs] -basepython = python3.8 +basepython = python3.10 deps = -r{toxinidir}/requirements-docs.txt -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt?h=master commands = - sphinx-build -W -q -b html -n -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/html + sphinx-build -q -b html -n -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/html [testenv:docs-linkcheck] -basepython = python3.8 +basepython = python3.10 deps = -r{toxinidir}/requirements-docs.txt -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt?h=master commands = - sphinx-build -W -q -b linkcheck -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/linkcheck + sphinx-build -q -b linkcheck -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/linkcheck [testenv:docs-spellcheck] -basepython = python3.8 +basepython = python3.10 deps = -r{toxinidir}/requirements-docs.txt -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt diff --git a/integration-test/pom.xml b/integration-test/pom.xml index af67a5f69..b2bdce757 100644 --- a/integration-test/pom.xml +++ b/integration-test/pom.xml @@ -23,7 +23,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy index e39e11440..f4cc8b733 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy @@ -90,7 +90,7 @@ class TestConfig extends Specification{ @Bean CpsModulePersistenceService cpsModulePersistenceService() { - return (CpsModulePersistenceService) new CpsModulePersistenceServiceImpl(yangResourceRepository, schemaSetRepository, dataspaceRepository, cpsAdminPersistenceService(), moduleReferenceRepository) + return (CpsModulePersistenceService) new CpsModulePersistenceServiceImpl(yangResourceRepository, schemaSetRepository, dataspaceRepository, moduleReferenceRepository) } @Bean diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy index cfc8ab7ad..d33a77446 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy @@ -94,7 +94,7 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase { moduleReferences.addAll(existingModuleReferences) when: 'the new schema set is created' def schemaSetName = "NewSchemaWith${numberOfNewModules}Modules" - objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences) + objectUnderTest.createOrUpgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences) and: 'associated with a new anchor' cpsAdminService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, 'newAnchor') then: 'the new anchor has the correct number of modules' diff --git a/jacoco-report/pom.xml b/jacoco-report/pom.xml index 22e071fcb..020cbc960 100644 --- a/jacoco-report/pom.xml +++ b/jacoco-report/pom.xml @@ -5,7 +5,7 @@ org.onap.cps cps-parent - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT ../cps-parent/pom.xml 4.0.0 diff --git a/pom.xml b/pom.xml index 71310fb58..9efdef2ad 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ org.onap.cps cps-aggregator - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT pom cps diff --git a/releases/3.3.8-container.yaml b/releases/3.3.8-container.yaml new file mode 100644 index 000000000..810f328b6 --- /dev/null +++ b/releases/3.3.8-container.yaml @@ -0,0 +1,8 @@ +distribution_type: container +container_release_tag: 3.3.8 +project: cps +log_dir: cps-maven-docker-stage-master/929/ +ref: cb9d15022ca5e7696e474ffc8c1b10fa053d6b40 +containers: + - name: 'cps-and-ncmp' + version: '3.3.8-20230928T113343Z' \ No newline at end of file diff --git a/releases/3.3.8.yaml b/releases/3.3.8.yaml new file mode 100644 index 000000000..e28b4532e --- /dev/null +++ b/releases/3.3.8.yaml @@ -0,0 +1,4 @@ +distribution_type: maven +log_dir: cps-maven-stage-master/937/ +project: cps +version: 3.3.8 \ No newline at end of file diff --git a/spotbugs/pom.xml b/spotbugs/pom.xml index d511c0fb9..743d3b795 100644 --- a/spotbugs/pom.xml +++ b/spotbugs/pom.xml @@ -25,7 +25,7 @@ 4.0.0 org.onap.cps spotbugs - 3.3.8-SNAPSHOT + 3.3.9-SNAPSHOT https://nexus.onap.org diff --git a/version.properties b/version.properties index 4709bf127..482fe366c 100755 --- a/version.properties +++ b/version.properties @@ -22,7 +22,7 @@ major=3 minor=3 -patch=8 +patch=9 base_version=${major}.${minor}.${patch}