diff options
19 files changed, 297 insertions, 28 deletions
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml index e3ffd04d7b..b5b10b0f74 100644 --- a/cps-application/src/main/resources/application.yml +++ b/cps-application/src/main/resources/application.yml @@ -98,6 +98,8 @@ app: ncmp:
async-m2m:
topic: ${NCMP_ASYNC_M2M_TOPIC:ncmp-async-m2m}
+ avc:
+ subscription-topic: ${NCMP_CM_AVC_SUBSCRIPTION:cm-avc-subscription}
lcm:
events:
topic: ${LCM_EVENTS_TOPIC:ncmp-events}
diff --git a/cps-ncmp-events/src/main/resources/schemas/avc-subscription-event-v1.json b/cps-ncmp-events/src/main/resources/schemas/avc-subscription-event-v1.json new file mode 100644 index 0000000000..5ab446cbbe --- /dev/null +++ b/cps-ncmp-events/src/main/resources/schemas/avc-subscription-event-v1.json @@ -0,0 +1,101 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "urn:cps:org.onap.cps.ncmp.events:avc-subscription-event:v1", + "$ref": "#/definitions/SubscriptionEvent", + "definitions": { + "SubscriptionEvent": { + "description": "The payload for avc subscription event.", + "type": "object", + "properties": { + "version": { + "description": "The event type version", + "type": "string" + }, + "eventType": { + "description": "The event type", + "type": "string", + "enum": ["CREATE"] + }, + "event": { + "$ref": "#/definitions/event" + } + }, + "required": [ + "version", + "eventContent" + ], + "additionalProperties": false + }, + "event": { + "description": "The event content.", + "type": "object", + "properties": { + "subscription": { + "description": "The subscription details.", + "type": "object", + "properties": { + "clientID": { + "description": "The clientID", + "type": "string" + }, + "name": { + "description": "The name of the subscription", + "type": "string" + }, + "isTagged": { + "description": "optional parameter, default is no", + "type": "boolean", + "default": false + } + }, + "required": [ + "clientID", + "name" + ] + }, + "dataType": { + "description": "The datatype content.", + "type": "object", + "properties": { + "dataspace": { + "description": "The dataspace name", + "type": "string" + }, + "dataCategory": { + "description": "The category type of the data", + "type": "string" + }, + "dataProvider": { + "description": "The provider name of the data", + "type": "string" + }, + "schemaName": { + "description": "The name of the schema", + "type": "string" + }, + "schemaVersion": { + "description": "The version of the schema", + "type": "string" + } + } + }, + "required": [ + "dataspace", + "dataCategory", + "dataProvider", + "schemaName", + "schemaVersion" + ], + "predicates": { + "description": "Additional values to be added into the subscription", + "existingJavaType" : "java.util.Map<String,Object>", + "type" : "object" + } + } + }, + "required": [ + "subscription", + "dataType" + ] + } +}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventConsumer.java new file mode 100644 index 0000000000..1f0324693a --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventConsumer.java @@ -0,0 +1,53 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.event.avc; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.event.model.SubscriptionEvent; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + + +@Component +@Slf4j +@RequiredArgsConstructor +public class SubscriptionEventConsumer { + + /** + * Consume the specified event. + * + * @param subscriptionEvent the event to be consumed + */ + @KafkaListener(topics = "${app.ncmp.avc.subscription-topic}") + public void consumeSubscriptionEvent(final SubscriptionEvent subscriptionEvent) { + if ("CM".equals(subscriptionEvent.getEvent().getDataType().getDataCategory())) { + log.debug("Consuming event {} ...", subscriptionEvent.toString()); + if ("CREATE".equals(subscriptionEvent.getEventType().value())) { + log.info("Subscription for ClientID {} with name{} ...", + subscriptionEvent.getEvent().getSubscription().getClientID(), + subscriptionEvent.getEvent().getSubscription().getName()); + } + } else { + log.trace("Non-CM subscription event ignored"); + } + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventConsumerSpec.groovy new file mode 100644 index 0000000000..20d60e3963 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventConsumerSpec.groovy @@ -0,0 +1,52 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2022 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.event.avc + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec +import org.onap.cps.ncmp.event.model.SubscriptionEvent +import org.onap.cps.ncmp.utils.TestUtils +import org.onap.cps.utils.JsonObjectMapper +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest(classes = [SubscriptionEventConsumer, ObjectMapper, JsonObjectMapper]) +class SubscriptionEventConsumerSpec extends MessagingBaseSpec { + + def objectUnderTest = new SubscriptionEventConsumer() + + @Autowired + JsonObjectMapper jsonObjectMapper + + def 'Consume valid message'() { + given: 'an event' + def jsonData = TestUtils.getResourceFileContent('avcSubscriptionCreationEvent.json') + def testEventSent = jsonObjectMapper.convertJsonString(jsonData, SubscriptionEvent.class) + and: 'dataCategory is set' + testEventSent.getEvent().getDataType().setDataCategory(dataCategory) + when: 'the valid event is consumed' + objectUnderTest.consumeSubscriptionEvent(testEventSent) + then: 'no exception is thrown' + noExceptionThrown() + where: 'data category is changed' + dataCategory << [ 'CM' , 'FM' ] + } +} diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml index 8d8bfaf9b4..4009e564a8 100644 --- a/cps-ncmp-service/src/test/resources/application.yml +++ b/cps-ncmp-service/src/test/resources/application.yml @@ -16,6 +16,11 @@ # SPDX-License-Identifier: Apache-2.0 # ============LICENSE_END========================================================= +app: + ncmp: + avc: + subscription-topic: test-avc-subscription + ncmp: dmi: auth: diff --git a/cps-ncmp-service/src/test/resources/avcSubscriptionCreationEvent.json b/cps-ncmp-service/src/test/resources/avcSubscriptionCreationEvent.json new file mode 100644 index 0000000000..1d84c3a5f2 --- /dev/null +++ b/cps-ncmp-service/src/test/resources/avcSubscriptionCreationEvent.json @@ -0,0 +1,23 @@ +{ + "version": "1.0", + "eventType": "CREATE", + "event": { + "subscription": { + "clientID": "SCO-9989752", + "name": "cm-subscription-001" + }, + "dataType": { + "dataspace": "ALL", + "dataCategory": "CM", + "dataProvider": "CM-SERVICE", + "schemaName": "org.onap.ncmp:cm-network-avc-event.rfc8641", + "schemaVersion": "1.0" + }, + "predicates": { + "datastore": "passthrough-operational", + "datastore-xpath-filter": "//_3gpp-nr-nrm-gnbdufunction:GNBDUFunction/ ", + "_3gpp-nr-nrm-nrcelldu": "NRCellDU" + + } + } +}
\ No newline at end of file diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java index 7183120120..3a9d70ebbc 100644 --- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java +++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java @@ -22,7 +22,9 @@ package org.onap.cps.cpspath.parser; import static org.onap.cps.cpspath.parser.CpsPathPrefixType.DESCENDANT; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.onap.cps.cpspath.parser.antlr4.CpsPathBaseListener; import org.onap.cps.cpspath.parser.antlr4.CpsPathParser; @@ -50,6 +52,8 @@ public class CpsPathBuilder extends CpsPathBaseListener { boolean processingAncestorAxis = false; + private List<String> containerNames = new ArrayList<>(); + @Override public void exitInvalidPostFix(final CpsPathParser.InvalidPostFixContext ctx) { throw new PathParsingException(ctx.getText()); @@ -146,6 +150,7 @@ public class CpsPathBuilder extends CpsPathBaseListener { CpsPathQuery build() { cpsPathQuery.setNormalizedXpath(normalizedXpathBuilder.toString()); + cpsPathQuery.setContainerNames(containerNames); return cpsPathQuery; } @@ -155,10 +160,12 @@ public class CpsPathBuilder extends CpsPathBaseListener { @Override public void exitContainerName(final CpsPathParser.ContainerNameContext ctx) { + final String containerName = ctx.getText(); normalizedXpathBuilder.append("/") - .append(ctx.getText()); + .append(containerName); + containerNames.add(containerName); if (processingAncestorAxis) { - normalizedAncestorPathBuilder.append("/").append(ctx.getText()); + normalizedAncestorPathBuilder.append("/").append(containerName); } } diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java index a9bd5d81c3..c9df8df904 100644 --- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java +++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java @@ -22,6 +22,7 @@ package org.onap.cps.cpspath.parser; import static org.onap.cps.cpspath.parser.CpsPathPrefixType.ABSOLUTE; +import java.util.List; import java.util.Map; import lombok.AccessLevel; import lombok.Getter; @@ -34,6 +35,7 @@ public class CpsPathQuery { private String xpathPrefix; private String normalizedParentPath; private String normalizedXpath; + private List<String> containerNames; private CpsPathPrefixType cpsPathPrefixType = ABSOLUTE; private String descendantName; private Map<String, Object> leavesData; diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java index 283463b512..60f0e2efcd 100644 --- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java +++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java @@ -22,6 +22,7 @@ package org.onap.cps.cpspath.parser; import static org.onap.cps.cpspath.parser.CpsPathPrefixType.ABSOLUTE; +import java.util.List; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -60,6 +61,11 @@ public class CpsPathUtil { return getCpsPathBuilder(xpathSource).build().getNormalizedParentPath(); } + public static String[] getXpathNodeIdSequence(final String xpathSource) { + final List<String> containerNames = getCpsPathBuilder(xpathSource).build().getContainerNames(); + return containerNames.toArray(new String[containerNames.size()]); + } + /** * Returns boolean indicating xpath is an absolute path to a list element. diff --git a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy index df2e9d72c6..f6a768a0a1 100644 --- a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy +++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy @@ -29,7 +29,7 @@ class CpsPathUtilSpec extends Specification { when: 'xpath with #scenario is parsed' def result = CpsPathUtil.getNormalizedXpath(xpath) then: 'normalized path uses single quotes for leave values' - result == "/parent/child[@common-leaf-name='123']" + assert result == "/parent/child[@common-leaf-name='123']" where: 'the following xpaths are used' scenario | xpath 'no quotes' | '/parent/child[@common-leaf-name=123]' @@ -41,7 +41,7 @@ class CpsPathUtilSpec extends Specification { when: 'a given xpath with #scenario is parsed' def result = CpsPathUtil.getNormalizedParentXpath(xpath) then: 'the result is the expected parent path' - result == expectedParentPath + assert result == expectedParentPath where: 'the following xpaths are used' scenario | xpath || expectedParentPath 'no child' | '/parent' || '' @@ -54,6 +54,22 @@ class CpsPathUtilSpec extends Specification { 'parent is list element using "' | '/parent/child[@id="x"]/grandChild' || "/parent/child[@id='x']" } + def 'Get node ID sequence for given xpath'() { + when: 'a given xpath with #scenario is parsed' + def result = CpsPathUtil.getXpathNodeIdSequence(xpath) + then: 'the result is the expected node ID sequence' + assert result == expectedNodeIdSequence + where: 'the following xpaths are used' + scenario | xpath || expectedNodeIdSequence + 'no child' | '/parent' || ["parent"] + 'child and parent' | '/parent/child' || ["parent","child"] + 'grand child' | '/parent/child/grandChild' || ["parent","child","grandChild"] + 'parent & top is list element' | '/parent[@id=1]/child' || ["parent","child"] + 'parent is list element' | '/parent/child[@id=1]/grandChild' || ["parent","child","grandChild"] + 'parent is list element with /' | "/parent/child[@id='a/b']/grandChild" || ["parent","child","grandChild"] + 'parent is list element with [' | "/parent/child[@id='a[b']/grandChild" || ["parent","child","grandChild"] + } + def 'Recognizing (absolute) xpaths to List elements'() { expect: 'check for list returns the correct values' assert CpsPathUtil.isPathToListElement(xpath) == expectList diff --git a/cps-service/pom.xml b/cps-service/pom.xml index 7173d8674b..70fa4479a4 100644 --- a/cps-service/pom.xml +++ b/cps-service/pom.xml @@ -116,6 +116,10 @@ <artifactId>janino</artifactId> </dependency> <dependency> + <groupId>org.onap.cps</groupId> + <artifactId>cps-path-parser</artifactId> + </dependency> + <dependency> <!-- Hazelcast provide Distributed Caches --> <groupId>com.hazelcast</groupId> <artifactId>hazelcast-spring</artifactId> diff --git a/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java b/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java index 49d3a70f9f..eb0c764cbc 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java +++ b/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java @@ -30,7 +30,6 @@ import java.io.IOException; import java.io.StringReader; import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -45,6 +44,8 @@ import javax.xml.stream.XMLStreamReader; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.onap.cps.cpspath.parser.CpsPathUtil; +import org.onap.cps.cpspath.parser.PathParsingException; import org.onap.cps.spi.exceptions.DataValidationException; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -74,9 +75,6 @@ import org.xml.sax.SAXException; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class YangUtils { - private static final String XPATH_DELIMITER_REGEX = "\\/"; - private static final String XPATH_NODE_KEY_ATTRIBUTES_REGEX = "\\[.*?\\]"; - /** * Parses data into Collection of NormalizedNode according to given schema context. * @@ -257,15 +255,12 @@ public class YangUtils { } private static String[] xpathToNodeIdSequence(final String xpath) { - final String[] xpathNodeIdSequence = Arrays.stream(xpath - .replaceAll(XPATH_NODE_KEY_ATTRIBUTES_REGEX, "") - .split(XPATH_DELIMITER_REGEX)) - .filter(identifier -> !identifier.isEmpty()) - .toArray(String[]::new); - if (xpathNodeIdSequence.length < 1) { - throw new DataValidationException("Invalid xpath.", "Xpath contains no node identifiers."); + try { + return CpsPathUtil.getXpathNodeIdSequence(xpath); + } catch (final PathParsingException pathParsingException) { + throw new DataValidationException(pathParsingException.getMessage(), pathParsingException.getDetails(), + pathParsingException); } - return xpathNodeIdSequence; } private static Map<String, Object> findDataSchemaNodeAndIdentifiersByXpathNodeIdSequence( diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy index e205a19eed..b70c437953 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy @@ -41,7 +41,7 @@ class JsonObjectMapperSpec extends Specification { then: 'the result is a valid json string (can be parsed)' def contentMap = new JsonSlurper().parseText(content) and: 'the parsed content is as expected' - assert contentMap.'test:bookstore'.'bookstore-name' == 'Chapters' + assert contentMap.'test:bookstore'.'bookstore-name' == 'Chapters/Easons' } def 'Map a structured object to json String error.'() { diff --git a/cps-service/src/test/resources/bookstore.json b/cps-service/src/test/resources/bookstore.json index d1b8d6882d..459908bd63 100644 --- a/cps-service/src/test/resources/bookstore.json +++ b/cps-service/src/test/resources/bookstore.json @@ -1,19 +1,19 @@ { "test:bookstore":{ - "bookstore-name": "Chapters", + "bookstore-name": "Chapters/Easons", "categories": [ { - "code": "01", + "code": "01/1", "name": "SciFi", "books": [ { "authors": [ "Iain M. Banks" ], - "lang": "en", + "lang": "en/it", "price": "895", "pub_year": "1994", - "title": "Feersum Endjinn" + "title": "Feersum Endjinn/Endjinn Feersum" }, { "authors": [ diff --git a/csit/data/test-tree.json b/csit/data/test-tree.json index 89d6784275..8f4b522799 100644 --- a/csit/data/test-tree.json +++ b/csit/data/test-tree.json @@ -2,11 +2,11 @@ "test-tree": { "branch": [ { - "name": "Left", + "name": "LEFT/left", "nest": { - "name": "Small", + "name": "SMALL/small", "birds": [ - "Sparrow", + "SPARROW/sparrow", "Robin", "Finch" ] diff --git a/csit/prepare-csit.sh b/csit/prepare-csit.sh index dde961697d..78f0fbde22 100755 --- a/csit/prepare-csit.sh +++ b/csit/prepare-csit.sh @@ -57,7 +57,8 @@ else rm -f ${WORKSPACE}/env.properties cd /tmp git clone "https://gerrit.onap.org/r/ci-management" - source /tmp/ci-management/jjb/integration/include-raw-integration-install-robotframework-py3.sh +# source /tmp/ci-management/jjb/integration/include-raw-integration-install-robotframework-py3.sh + source ${WORKSPACE}/install-robotframework.sh fi # install eteutils diff --git a/csit/pylibs.txt b/csit/pylibs.txt index 4952616540..9fee634156 100644 --- a/csit/pylibs.txt +++ b/csit/pylibs.txt @@ -6,7 +6,7 @@ pyhocon requests robotframework-httplibrary robotframework-requests==0.9.3 -robotframework-selenium2library +robotframework-selenium2library==3.0.0 robotframework-extendedselenium2library robotframework-sshlibrary scapy diff --git a/csit/tests/cps-data/cps-data.robot b/csit/tests/cps-data/cps-data.robot index 2da2b73414..096bd07b79 100644 --- a/csit/tests/cps-data/cps-data.robot +++ b/csit/tests/cps-data/cps-data.robot @@ -44,10 +44,10 @@ Create Data Node Get Data Node by XPath ${uri}= Set Variable ${basePath}/v1/dataspaces/${dataspaceName}/anchors/${anchorName}/node - ${params}= Create Dictionary xpath=/test-tree/branch[@name='Left']/nest + ${params}= Create Dictionary xpath=/test-tree/branch[@name='LEFT/left']/nest ${headers}= Create Dictionary Authorization=${auth} ${response}= Get On Session CPS_URL ${uri} params=${params} headers=${headers} expected_status=200 ${responseJson}= Set Variable ${response.json()['tree:nest']} - Should Be Equal As Strings ${responseJson['name']} Small + Should Be Equal As Strings ${responseJson['name']} SMALL/small diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 32219000b5..9aaf0501d5 100755 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -50,6 +50,8 @@ Bug Fixes 3.2.0 - `CPS-1312 <https://jira.onap.org/browse/CPS-1312>`_ CPS(/NCMP) does not have version control - `CPS-1350 <https://jira.onap.org/browse/CPS-1350>`_ [CPS/NCMP] Add Basic Auth to CPS/NCMP OpenAPI Definitions + - `CPS-1433 <https://jira.onap.org/browse/CPS-1433>`_ [CPS/NCMP] Fix to allow posting data with '/' + - `CPS-1409 <https://jira.onap.org/browse/CPS-1409>`_ [CPS/NCMP] Fix Delete uses case with '/' in path Known Limitations, Issues and Workarounds ----------------------------------------- |