summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToineSiebelink <toine.siebelink@est.tech>2021-05-20 16:44:21 +0100
committerToineSiebelink <toine.siebelink@est.tech>2021-06-01 10:12:55 +0100
commitc37678a3eb62685d32a1581729e2a4e26002bffc (patch)
tree1901f7e3517ae339f99905f7ffc0021553874842
parent9de3b68373dd8554e64f34bb3093403521f8759f (diff)
Introducing Antlr4 for cpsPath parsing
-created new module for cpPathParser -added antlr rule for cpsPathWithSingleLeafCondition -added antlr rule for cpsPathWithDescendant (and with leaf conditions) -added antlr rule for ancestor axis -added unit test (copied from existing CpsPathQuerySpec) -udpated cps-ri to use new cpPathQuery from parser module -'imported' lexer rules from publix xPath grammar -Re-used existing CpsPathException but conversion happens in cps-ri to prevent additional dependency in cps-path-parser module Issue-ID: CPS-376 Change-Id: I2c5df98969402cbf69f6573c52705879450ce606 Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
-rwxr-xr-xcps-dependencies/pom.xml23
-rwxr-xr-xcps-parent/pom.xml1
-rw-r--r--cps-path-parser/lombok.config2
-rw-r--r--cps-path-parser/pom.xml91
-rw-r--r--cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4123
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java107
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java79
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQueryType.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/query/CpsPathQueryType.java)6
-rw-r--r--cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy (renamed from cps-ri/src/test/groovy/org/onap/cps/spi/query/CpsPathQuerySpec.groovy)80
-rw-r--r--cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy23
-rw-r--r--cps-ri/pom.xml5
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java14
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/query/CpsPathQuery.java161
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy22
-rw-r--r--cps-service/src/main/java/org/onap/cps/spi/exceptions/CpsPathException.java7
-rwxr-xr-xcps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy12
-rw-r--r--pom.xml1
-rw-r--r--spotbugs/src/main/resources/spotbugs-exclude.xml4
18 files changed, 523 insertions, 238 deletions
diff --git a/cps-dependencies/pom.xml b/cps-dependencies/pom.xml
index 73aa36acc..16a6532a9 100755
--- a/cps-dependencies/pom.xml
+++ b/cps-dependencies/pom.xml
@@ -1,4 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ Copyright (c) 2021 Linux 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.
+ ============LICENSE_END=========================================================
+-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -13,6 +30,7 @@
<description>This artifact contains dependencyManagement declarations of upstream versions.</description>
<properties>
+ <antlr4-runtime.version>4.9.2</antlr4-runtime.version>
<cglib-nodep.version>3.1</cglib-nodep.version>
<commons-codec.version>1.15</commons-codec.version>
<commons-lang3.version>3.11</commons-lang3.version>
@@ -93,6 +111,11 @@
<version>${hibernate-types.version}</version>
</dependency>
<dependency>
+ <groupId>org.antlr</groupId>
+ <artifactId>antlr4-runtime</artifactId>
+ <version>${antlr4-runtime.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>${groovy.version}</version>
diff --git a/cps-parent/pom.xml b/cps-parent/pom.xml
index 3552c28c9..18a985647 100755
--- a/cps-parent/pom.xml
+++ b/cps-parent/pom.xml
@@ -347,6 +347,7 @@
<configuration>
<excludes>
<exclude>org/onap/cps/rest/model/*</exclude>
+ <exclude>org/onap/cps/cpspath/parser/antlr4/*</exclude>
</excludes>
<dataFile>${project.build.directory}/code-coverage/jacoco-ut.exec</dataFile>
<rules>
diff --git a/cps-path-parser/lombok.config b/cps-path-parser/lombok.config
new file mode 100644
index 000000000..a23edb413
--- /dev/null
+++ b/cps-path-parser/lombok.config
@@ -0,0 +1,2 @@
+config.stopBubbling = true
+lombok.addLombokGeneratedAnnotation = true \ No newline at end of file
diff --git a/cps-path-parser/pom.xml b/cps-path-parser/pom.xml
new file mode 100644
index 000000000..38ba9acf3
--- /dev/null
+++ b/cps-path-parser/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ Copyright (c) 2021 Linux 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.
+ ============LICENSE_END=========================================================
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-parent</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ <relativePath>../cps-parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>cps-path-parser</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.antlr</groupId>
+ <artifactId>antlr4-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>antlr4</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.antlr</groupId>
+ <artifactId>antlr4-runtime</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ </dependency>
+ <!-- T E S T D E P E N D E N C I E S -->
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-spring</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib-nodep</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.junit.vintage</groupId>
+ <artifactId>junit-vintage-engine</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4
new file mode 100644
index 000000000..86095459e
--- /dev/null
+++ b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4
@@ -0,0 +1,123 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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=========================================================
+ */
+
+grammar CpsPath ;
+
+cpsPath: (cpsPathWithSingleLeafCondition | cpsPathWithDescendant | cpsPathWithDescendantAndLeafConditions) ancestorAxis? ;
+
+ancestorAxis: SLASH KW_ANCESTOR COLONCOLON ancestorPath ;
+
+ancestorPath: yangElement (SLASH yangElement)* ;
+
+cpsPathWithSingleLeafCondition: prefix singleValueCondition ;
+
+/*
+No need to ditinguish between cpsPathWithDescendant | cpsPathWithDescendantAndLeafConditions really!
+See https://jira.onap.org/browse/CPS-436
+*/
+
+cpsPathWithDescendant: descendant ;
+
+cpsPathWithDescendantAndLeafConditions: descendant multipleValueConditions ;
+
+descendant: SLASH prefix ;
+
+prefix: (SLASH yangElement)* SLASH containerName ;
+
+yangElement: containerName listElementRef? ;
+
+containerName: QName ;
+
+listElementRef: multipleValueConditions ;
+
+singleValueCondition: '[' leafCondition ']' ;
+
+multipleValueConditions: '[' leafCondition (' and ' leafCondition)* ']' ;
+
+leafCondition: '@' leafName '=' (IntegerLiteral | StringLiteral ) ;
+
+//To Confirm: defintion of Lefname with external xPath grammar
+leafName: QName ;
+
+/*
+ * Lexer Rules
+ * Most of the lexer rules below are 'imporetd' from
+ * https://raw.githubusercontent.com/antlr/grammars-v4/master/xpath/xpath31/XPath31.g4
+ */
+
+SLASH : '/';
+COLONCOLON : '::' ;
+
+// KEYWORDS
+
+KW_ANCESTOR : 'ancestor' ;
+
+IntegerLiteral : FragDigits ;
+// Add below type definitions for leafvalue comparision in https://jira.onap.org/browse/CPS-440
+DecimalLiteral : ('.' FragDigits) | (FragDigits '.' [0-9]*) ;
+DoubleLiteral : (('.' FragDigits) | (FragDigits ('.' [0-9]*)?)) [eE] [+-]? FragDigits ;
+StringLiteral : ('"' (FragEscapeQuot | ~[^"])*? '"') | ('\'' (FragEscapeApos | ~['])*? '\'') ;
+fragment FragEscapeQuot : '""' ;
+fragment FragEscapeApos : '\'';
+fragment FragDigits : [0-9]+ ;
+
+QName : FragQName ;
+NCName : FragmentNCName ;
+fragment FragQName : FragPrefixedName | FragUnprefixedName ;
+fragment FragPrefixedName : FragPrefix ':' FragLocalPart ;
+fragment FragUnprefixedName : FragLocalPart ;
+fragment FragPrefix : FragmentNCName ;
+fragment FragLocalPart : FragmentNCName ;
+fragment FragNCNameStartChar
+ : 'A'..'Z'
+ | '_'
+ | 'a'..'z'
+ | '\u00C0'..'\u00D6'
+ | '\u00D8'..'\u00F6'
+ | '\u00F8'..'\u02FF'
+ | '\u0370'..'\u037D'
+ | '\u037F'..'\u1FFF'
+ | '\u200C'..'\u200D'
+ | '\u2070'..'\u218F'
+ | '\u2C00'..'\u2FEF'
+ | '\u3001'..'\uD7FF'
+ | '\uF900'..'\uFDCF'
+ | '\uFDF0'..'\uFFFD'
+ | '\u{10000}'..'\u{EFFFF}'
+ ;
+fragment FragNCNameChar
+ : FragNCNameStartChar | '-' | '.' | '0'..'9'
+ | '\u00B7' | '\u0300'..'\u036F'
+ | '\u203F'..'\u2040'
+ ;
+fragment FragmentNCName : FragNCNameStartChar FragNCNameChar* ;
+
+// https://www.w3.org/TR/REC-xml/#NT-Char
+
+fragment FragChar : '\u0009' | '\u000a' | '\u000d'
+ | '\u0020'..'\ud7ff'
+ | '\ue000'..'\ufffd'
+ | '\u{10000}'..'\u{10ffff}'
+ ;
+
+// Skip all Whitespace
+Whitespace : ('\u000d' | '\u000a' | '\u0020' | '\u0009')+ -> skip ;
+
+// handle characters which failed to match any other token (otherwise Antlr will ignore them)
+ErrorCharacter : . ;
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
new file mode 100644
index 000000000..83e076d53
--- /dev/null
+++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java
@@ -0,0 +1,107 @@
+package org.onap.cps.cpspath.parser;
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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=========================================================
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathBaseListener;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.AncestorAxisContext;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.CpsPathWithDescendantAndLeafConditionsContext;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.CpsPathWithDescendantContext;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.CpsPathWithSingleLeafConditionContext;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.LeafConditionContext;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.MultipleValueConditionsContext;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.PrefixContext;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.SingleValueConditionContext;
+
+public class CpsPathBuilder extends CpsPathBaseListener {
+
+ final CpsPathQuery cpsPathQuery = new CpsPathQuery();
+
+ final Map<String, Object> leavesData = new HashMap<>();
+
+ @Override
+ public void exitPrefix(final PrefixContext ctx) {
+ cpsPathQuery.setXpathPrefix(ctx.getText());
+ }
+
+ @Override
+ public void exitLeafCondition(final LeafConditionContext ctx) {
+ Object comparisonValue = null;
+ if (ctx.IntegerLiteral() != null) {
+ comparisonValue = Integer.valueOf(ctx.IntegerLiteral().getText());
+ }
+ if (ctx.StringLiteral() != null) {
+ comparisonValue = stripFirstAndLastCharacter(ctx.StringLiteral().getText());
+ } else if (comparisonValue == null) {
+ throw new IllegalStateException("Unsupported comparison value encountered in expression" + ctx.getText());
+ }
+ leavesData.put(ctx.leafName().getText(), comparisonValue);
+ }
+
+ @Override
+ public void enterSingleValueCondition(final SingleValueConditionContext ctx) {
+ leavesData.clear();
+ }
+
+ @Override
+ public void enterMultipleValueConditions(final MultipleValueConditionsContext ctx) {
+ leavesData.clear();
+ }
+
+ @Override
+ public void exitSingleValueCondition(final SingleValueConditionContext ctx) {
+ final String leafName = ctx.leafCondition().leafName().getText();
+ cpsPathQuery.setLeafName(leafName);
+ cpsPathQuery.setLeafValue(leavesData.get(leafName));
+ }
+
+ @Override
+ public void exitCpsPathWithSingleLeafCondition(final CpsPathWithSingleLeafConditionContext ctx) {
+ cpsPathQuery.setCpsPathQueryType(CpsPathQueryType.XPATH_LEAF_VALUE);
+ }
+
+ @Override
+ public void exitCpsPathWithDescendant(final CpsPathWithDescendantContext ctx) {
+ cpsPathQuery.setCpsPathQueryType(CpsPathQueryType.XPATH_HAS_DESCENDANT_ANYWHERE);
+ cpsPathQuery.setDescendantName(cpsPathQuery.getXpathPrefix().substring(1));
+ }
+
+ @Override
+ public void exitCpsPathWithDescendantAndLeafConditions(
+ final CpsPathWithDescendantAndLeafConditionsContext ctx) {
+ cpsPathQuery.setCpsPathQueryType(CpsPathQueryType.XPATH_HAS_DESCENDANT_WITH_LEAF_VALUES);
+ cpsPathQuery.setDescendantName(cpsPathQuery.getXpathPrefix().substring(1));
+ cpsPathQuery.setLeavesData(leavesData);
+ }
+
+ @Override
+ public void exitAncestorAxis(final AncestorAxisContext ctx) {
+ cpsPathQuery.setAncestorSchemaNodeIdentifier(ctx.ancestorPath().getText());
+ }
+
+ CpsPathQuery build() {
+ return cpsPathQuery;
+ }
+
+ private static String stripFirstAndLastCharacter(final String wrappedString) {
+ return wrappedString.substring(1, wrappedString.length() - 1);
+ }
+
+}
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
new file mode 100644
index 000000000..32fe0cbb7
--- /dev/null
+++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java
@@ -0,0 +1,79 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.cpspath.parser;
+
+
+import java.util.Map;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathLexer;
+import org.onap.cps.cpspath.parser.antlr4.CpsPathParser;
+
+@Getter
+@Setter(AccessLevel.PACKAGE)
+public class CpsPathQuery {
+
+ private CpsPathQueryType cpsPathQueryType;
+ private String xpathPrefix;
+ private String leafName;
+ private Object leafValue;
+ private String descendantName;
+ private Map<String, Object> leavesData;
+ private String ancestorSchemaNodeIdentifier = "";
+
+ /**
+ * Returns a cps path query.
+ *
+ * @param cpsPathSource cps path
+ * @return a CpsPathQuery object.
+ */
+ public static CpsPathQuery createFrom(final String cpsPathSource) {
+ final var inputStream = CharStreams.fromString(cpsPathSource);
+ final var cpsPathLexer = new CpsPathLexer(inputStream);
+ final var cpsPathParser = new CpsPathParser(new CommonTokenStream(cpsPathLexer));
+ cpsPathParser.addErrorListener(new BaseErrorListener() {
+ @Override
+ public void syntaxError(final Recognizer<?, ?> recognizer, final Object offendingSymbol, final int line,
+ final int charPositionInLine, final String msg, final RecognitionException e) {
+ throw new IllegalStateException("failed to parse at line " + line + " due to " + msg, e);
+ }
+ });
+ final var cpsPathBuilder = new CpsPathBuilder();
+ cpsPathParser.addParseListener(cpsPathBuilder);
+ cpsPathParser.cpsPath();
+ return cpsPathBuilder.build();
+ }
+
+ /**
+ * Has ancestor axis been populated.
+ *
+ * @return boolean value.
+ */
+ public boolean hasAncestorAxis() {
+ return !(ancestorSchemaNodeIdentifier.isEmpty());
+ }
+
+}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/query/CpsPathQueryType.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQueryType.java
index 3ae54ba22..bc6b9f092 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/query/CpsPathQueryType.java
+++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQueryType.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.query;
+package org.onap.cps.cpspath.parser;
/**
* The enum Cps path query type.
@@ -29,11 +29,11 @@ public enum CpsPathQueryType {
*/
XPATH_HAS_DESCENDANT_ANYWHERE,
/**
- * Xpath descendant anywhere type e.g. //nodeName[@leafName=leafValue] .
+ * Xpath descendant anywhere type e.g. //nodeName[@leafName="value"] .
*/
XPATH_HAS_DESCENDANT_WITH_LEAF_VALUES,
/**
- * Xpath leaf value cps path query type e.g. /cps-path[@leafName=leafValue] .
+ * Xpath leaf value cps path query type e.g. /cps-path[@leaf1="leafValue" and @leaf2=123] .
*/
XPATH_LEAF_VALUE
}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/query/CpsPathQuerySpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
index ee641d102..0e7fc35cf 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/query/CpsPathQuerySpec.groovy
+++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
@@ -1,7 +1,6 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2021 Nordix Foundation
- * Modifications Copyright (C) 2020-2021 Bell Canada.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,18 +17,15 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.query
+package org.onap.cps.cpspath.parser
-import org.onap.cps.spi.exceptions.CpsPathException
import spock.lang.Specification
class CpsPathQuerySpec extends Specification {
- def objectUnderTest = new CpsPathQuery()
-
def 'Parse cps path with valid cps path and a filter with #scenario.'() {
when: 'the given cps path is parsed'
- def result = objectUnderTest.createFrom(cpsPath)
+ def result = CpsPathQuery.createFrom(cpsPath)
then: 'the query has the right type'
result.cpsPathQueryType == CpsPathQueryType.XPATH_LEAF_VALUE
and: 'the right query parameters are set'
@@ -37,29 +33,31 @@ class CpsPathQuerySpec extends Specification {
result.leafName == expectedLeafName
result.leafValue == expectedLeafValue
where: 'the following data is used'
- scenario | cpsPath || expectedXpathPrefix | expectedLeafName | expectedLeafValue
- 'leaf of type String' | '/parent/child[@common-leaf-name=\'common-leaf-value\']' || '/parent/child' | 'common-leaf-name' | 'common-leaf-value'
- 'leaf of type Integer' | '/parent/child[@common-leaf-name-int=5]' || '/parent/child' | 'common-leaf-name-int' | 5
- 'spaces around =' | '/parent/child[@common-leaf-name-int = 5]' || '/parent/child' | 'common-leaf-name-int' | 5
- 'key in top container' | '/parent[@common-leaf-name-int=5]' || '/parent' | 'common-leaf-name-int' | 5
+ scenario | cpsPath || expectedXpathPrefix | expectedLeafName | expectedLeafValue
+ 'leaf of type String' | '/parent/child[@common-leaf-name="common-leaf-value"]' || '/parent/child' | 'common-leaf-name' | 'common-leaf-value'
+ 'leaf of type String' | '/parent/child[@common-leaf-name=\'common-leaf-value\']' || '/parent/child' | 'common-leaf-name' | 'common-leaf-value'
+ 'leaf of type Integer' | '/parent/child[@common-leaf-name-int=5]' || '/parent/child' | 'common-leaf-name-int' | 5
+ 'spaces around =' | '/parent/child[@common-leaf-name-int = 5]' || '/parent/child' | 'common-leaf-name-int' | 5
+ 'key in top container' | '/parent[@common-leaf-name-int=5]' || '/parent' | 'common-leaf-name-int' | 5
+ 'parent list' | '/shops/shop[@id=1]/categories[@id=1]/book[@title="Dune"]' || '/shops/shop[@id=1]/categories[@id=1]/book' | 'title' | 'Dune'
}
def 'Parse cps path of type ends with a #scenario.'() {
when: 'the given cps path is parsed'
- def result = objectUnderTest.createFrom(cpsPath)
+ def result = CpsPathQuery.createFrom(cpsPath)
then: 'the query has the right type'
result.cpsPathQueryType == CpsPathQueryType.XPATH_HAS_DESCENDANT_ANYWHERE
and: 'the right ends with parameters are set'
- result.descendantName == expectedEndsWithValue
+ result.descendantName == expectedDescendantName
where: 'the following data is used'
- scenario | cpsPath || expectedEndsWithValue
+ scenario | cpsPath || expectedDescendantName
'yang container' | '//cps-path' || 'cps-path'
'parent & child' | '//parent/child' || 'parent/child'
}
def 'Parse cps path that ends with a yang list containing #scenario.'() {
when: 'the given cps path is parsed'
- def result = objectUnderTest.createFrom(cpsPath)
+ def result = CpsPathQuery.createFrom(cpsPath)
then: 'the query has the right type'
result.cpsPathQueryType == CpsPathQueryType.XPATH_HAS_DESCENDANT_WITH_LEAF_VALUES
and: 'the right ends with parameters are set'
@@ -71,11 +69,11 @@ class CpsPathQuerySpec extends Specification {
'more than one attribute' | '//child[@int-leaf=5 and @leaf-name="leaf value"]' || 2
}
- def 'Parse cps path with #scenario.'() {
+ def 'Parse cps path with error: #scenario.'() {
when: 'the given cps path is parsed'
- objectUnderTest.createFrom(cpsPath)
+ CpsPathQuery.createFrom(cpsPath)
then: 'a CpsPathException is thrown'
- thrown(CpsPathException)
+ thrown(IllegalStateException)
where: 'the following data is used'
scenario | cpsPath
'no / at the start' | 'invalid-cps-path/child'
@@ -83,34 +81,44 @@ class CpsPathQuerySpec extends Specification {
'float value' | '/parent/child[@someFloat=5.0]'
'unmatched quotes, double quote first ' | '/parent/child[@someString="value with unmatched quotes\']'
'unmatched quotes, single quote first' | '/parent/child[@someString=\'value with unmatched quotes"]'
- 'too many containers' | '/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41/42/43/44/45/46/47/48/49/50/51/52/53/54/55/56/57/58/59/60/61/62/63/64/65/66/67/68/69/70/71/72/73/74/75/76/77/78/79/80/81/82/83/84/85/86/87/88/89/90/91/92/93/94/95/96/97/98/99/100[@a=1]'
'end with descendant and more than one attribute separated by "or"' | '//child[@int-leaf=5 or @leaf-name="leaf value"]'
'missing attribute value' | '//child[@int-leaf=5 and @name]'
'incomplete ancestor value' | '//books/ancestor::'
}
- def 'Convert cps leaf value to valid type with leaf of type #scenario.'() {
- when: 'the given leaf value is converted'
- def result = objectUnderTest.convertLeafValueToCorrectType(leafValueInputString, 'source xPath (for error message only)')
- then: 'the leaf value returned is of the right type'
- result == expectedLeafOutputValue
- where: "the following data is used"
- scenario | leafValueInputString || expectedLeafOutputValue
- 'Integer' | "5" || 5
- 'String with single quotes' | '\'value in single quotes\'' || 'value in single quotes'
- 'String with double quotes' | '"value in double quotes"' || 'value in double quotes'
- 'String containing single quote' | '"value with \'"' || 'value with \''
- 'String containing double quote' | '\'value with "\'' || 'value with "'
- }
-
- def 'Parse cps path using ancestor by schema node identifier.'() {
+ def 'Parse cps path using ancestor by schema node identifier with a #scenario.'() {
when: 'the given cps path is parsed'
- def result = objectUnderTest.createFrom('//someXpath/ancestor::someAncestor')
+ def result = CpsPathQuery.createFrom('//descendant/ancestor::' + ancestorPath)
then: 'the query has the right type'
result.cpsPathQueryType == CpsPathQueryType.XPATH_HAS_DESCENDANT_ANYWHERE
+ and: 'the result has ancestor axis'
+ result.hasAncestorAxis()
and: 'the correct ancestor schema node identifier is set'
- result.ancestorSchemaNodeIdentifier == 'someAncestor'
+ result.ancestorSchemaNodeIdentifier == ancestorPath
+ where:
+ scenario | ancestorPath
+ 'basic container' | 'someContainer'
+ 'container with parent' | 'parent/child'
+ 'ancestor that is a list' | 'categories[@code=1]'
+ 'parent that is a list' | 'parent[@id=1]/child'
+ }
+
+ def 'Combinations #scenario.'() {
+ when: 'the given cps path is parsed'
+ def result = CpsPathQuery.createFrom(cpsPath + '/ancestor::someAncestor')
+ then: 'the query has the right type'
+ result.cpsPathQueryType == expectedCpsPathQueryType
and: 'the result has ancestor axis'
result.hasAncestorAxis()
+ and: 'the correct ancestor schema node identifier is set'
+ result.ancestorSchemaNodeIdentifier == 'someAncestor'
+ result.descendantName == expectedDescendantName
+ where:
+ scenario | cpsPath || expectedDescendantName | expectedCpsPathQueryType
+ 'basic container' | '//someContainer' || 'someContainer' | CpsPathQueryType.XPATH_HAS_DESCENDANT_ANYWHERE
+ 'container with parent' | '//parent/child' || 'parent/child' | CpsPathQueryType.XPATH_HAS_DESCENDANT_ANYWHERE
+ 'container with list-parent' | '//parent[@id=1]/child' || 'parent[@id=1]/child' | CpsPathQueryType.XPATH_HAS_DESCENDANT_ANYWHERE
+ 'container with list-parent' | '//parent[@id=1]/child[@name="test"]' || 'parent[@id=1]/child' | CpsPathQueryType.XPATH_HAS_DESCENDANT_WITH_LEAF_VALUES
}
+
}
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy
index b4872908f..7da3df265 100644
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy
@@ -21,13 +21,6 @@
package org.onap.cps.rest.exceptions
-import static org.springframework.http.HttpStatus.BAD_REQUEST
-import static org.springframework.http.HttpStatus.CONFLICT
-import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
-import static org.springframework.http.HttpStatus.NOT_FOUND
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
-
import groovy.json.JsonSlurper
import org.modelmapper.ModelMapper
import org.onap.cps.api.CpsAdminService
@@ -52,6 +45,13 @@ import org.springframework.test.web.servlet.MockMvc
import spock.lang.Shared
import spock.lang.Specification
+import static org.springframework.http.HttpStatus.BAD_REQUEST
+import static org.springframework.http.HttpStatus.CONFLICT
+import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
+import static org.springframework.http.HttpStatus.NOT_FOUND
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
+
@WebMvcTest
class CpsRestExceptionHandlerSpec extends Specification {
@@ -128,11 +128,12 @@ class CpsRestExceptionHandlerSpec extends Specification {
setupTestException(exceptionThrown)
def response = performTestRequest()
then: 'an HTTP Bad Request response is returned with correct message and details'
- assertTestResponse(response, BAD_REQUEST, errorMessage, errorDetails)
+ assertTestResponse(response, BAD_REQUEST, expectedErrorMessage, expectedErrorDetails)
where: 'the following exceptions are thrown'
- exceptionThrown << [new ModelValidationException(errorMessage, errorDetails, null),
- new DataValidationException(errorMessage, errorDetails, null),
- new CpsPathException(errorMessage, errorDetails)]
+ exceptionThrown || expectedErrorMessage | expectedErrorDetails
+ new ModelValidationException(errorMessage, errorDetails, null) || errorMessage | errorDetails
+ new DataValidationException(errorMessage, errorDetails, null) || errorMessage | errorDetails
+ new CpsPathException(errorDetails) || CpsPathException.ERROR_MESSAGE | errorDetails
}
def 'Delete request with a #exceptionThrown.class.simpleName returns HTTP Status Conflict'() {
diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml
index 94d2fa19b..14e720eb6 100644
--- a/cps-ri/pom.xml
+++ b/cps-ri/pom.xml
@@ -17,6 +17,11 @@
<artifactId>cps-service</artifactId>
</dependency>
<dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>cps-path-parser</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
index 10440924e..af1eca33e 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
@@ -36,16 +36,17 @@ import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.transaction.Transactional;
+import org.onap.cps.cpspath.parser.CpsPathQuery;
+import org.onap.cps.cpspath.parser.CpsPathQueryType;
import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.entities.AnchorEntity;
import org.onap.cps.spi.entities.DataspaceEntity;
import org.onap.cps.spi.entities.FragmentEntity;
import org.onap.cps.spi.exceptions.AlreadyDefinedException;
+import org.onap.cps.spi.exceptions.CpsPathException;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.DataNodeBuilder;
-import org.onap.cps.spi.query.CpsPathQuery;
-import org.onap.cps.spi.query.CpsPathQueryType;
import org.onap.cps.spi.repository.AnchorRepository;
import org.onap.cps.spi.repository.DataspaceRepository;
import org.onap.cps.spi.repository.FragmentRepository;
@@ -171,7 +172,12 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
final FetchDescendantsOption fetchDescendantsOption) {
final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final var anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
- final var cpsPathQuery = CpsPathQuery.createFrom(cpsPath);
+ final CpsPathQuery cpsPathQuery;
+ try {
+ cpsPathQuery = CpsPathQuery.createFrom(cpsPath);
+ } catch (final IllegalStateException e) {
+ throw new CpsPathException(e.getMessage());
+ }
List<FragmentEntity> fragmentEntities;
if (CpsPathQueryType.XPATH_LEAF_VALUE.equals(cpsPathQuery.getCpsPathQueryType())) {
fragmentEntities = fragmentRepository
@@ -283,7 +289,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
fragmentRepository.save(fragmentEntity);
}
- private boolean isRootXpath(final String xpath) {
+ private static boolean isRootXpath(final String xpath) {
return "/".equals(xpath) || "".equals(xpath);
}
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/query/CpsPathQuery.java b/cps-ri/src/main/java/org/onap/cps/spi/query/CpsPathQuery.java
deleted file mode 100644
index 401667eaa..000000000
--- a/cps-ri/src/main/java/org/onap/cps/spi/query/CpsPathQuery.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021 Nordix Foundation
- * Modifications Copyright (C) 2021 Bell Canada.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.spi.query;
-
-import static org.springframework.util.StringUtils.isEmpty;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.Setter;
-import org.onap.cps.spi.exceptions.CpsPathException;
-
-@Getter
-@Setter(AccessLevel.PRIVATE)
-public class CpsPathQuery {
-
- private CpsPathQueryType cpsPathQueryType;
- private String xpathPrefix;
- private String leafName;
- private Object leafValue;
- private String descendantName;
- private Map<String, Object> leavesData;
- private String ancestorSchemaNodeIdentifier;
-
- private static final String NON_CAPTURING_GROUP_1_TO_99_YANG_CONTAINERS = "((?:\\/[^\\/]+){1,99})";
-
- private static final String YANG_LEAF_VALUE_EQUALS_CONDITION =
- "\\[\\s{0,9}@(\\S+?)\\s{0,9}=\\s{0,9}(.*?)\\s{0,9}\\]";
-
- private static final Pattern QUERY_CPS_PATH_WITH_SINGLE_LEAF_PATTERN =
- Pattern.compile(NON_CAPTURING_GROUP_1_TO_99_YANG_CONTAINERS + YANG_LEAF_VALUE_EQUALS_CONDITION);
-
- private static final Pattern DESCENDANT_ANYWHERE_PATTERN = Pattern.compile("\\/\\/([^\\/][^:]+)");
-
- private static final Pattern LEAF_INTEGER_VALUE_PATTERN = Pattern.compile("[-+]?\\d+");
-
- private static final Pattern LEAF_STRING_VALUE_IN_SINGLE_QUOTES_PATTERN = Pattern.compile("'(.*)'");
- private static final Pattern LEAF_STRING_VALUE_IN_DOUBLE_QUOTES_PATTERN = Pattern.compile("\"(.*)\"");
-
- private static final String YANG_MULTIPLE_LEAF_VALUE_EQUALS_CONDITION = "\\[(.*?)\\s{0,9}]";
-
- private static final Pattern DESCENDANT_ANYWHERE_PATTERN_WITH_MULTIPLE_LEAF_PATTERN =
- Pattern.compile(DESCENDANT_ANYWHERE_PATTERN + YANG_MULTIPLE_LEAF_VALUE_EQUALS_CONDITION);
-
- private static final String INDIVIDUAL_LEAF_DETAIL_PATTERN = ("\\s{1,9}and\\s{1,9}");
-
- private static final Pattern LEAF_VALUE_PATTERN = Pattern.compile("@(?>(\\S+?)=(.*))");
-
- private static final Pattern ANCESTOR_AXIS_PATTERN = Pattern.compile("(?>(\\S+)\\/ancestor::\\/?(\\S+))");
-
- /**
- * Returns a cps path query.
- *
- * @param cpsPathSource cps path
- * @return a CpsPath object.
- */
- public static CpsPathQuery createFrom(final String cpsPathSource) {
- var cpsPath = cpsPathSource;
- final var cpsPathQuery = new CpsPathQuery();
- var matcher = ANCESTOR_AXIS_PATTERN.matcher(cpsPath);
- if (matcher.matches()) {
- cpsPath = matcher.group(1);
- cpsPathQuery.setAncestorSchemaNodeIdentifier(matcher.group(2));
- }
- matcher = QUERY_CPS_PATH_WITH_SINGLE_LEAF_PATTERN.matcher(cpsPath);
- if (matcher.matches()) {
- cpsPathQuery.setParametersForSingleLeafValue(cpsPath, matcher);
- return cpsPathQuery;
- }
- matcher = DESCENDANT_ANYWHERE_PATTERN_WITH_MULTIPLE_LEAF_PATTERN.matcher(cpsPath);
- if (matcher.matches()) {
- cpsPathQuery.setParametersForDescendantWithLeafValues(cpsPath, matcher);
- return cpsPathQuery;
- }
- matcher = DESCENDANT_ANYWHERE_PATTERN.matcher(cpsPath);
- if (matcher.matches()) {
- cpsPathQuery.setParametersForDescendantAnywhere(matcher);
- return cpsPathQuery;
- }
- throw new CpsPathException("Invalid cps path.",
- String.format("Cannot interpret or parse cps path '%s'.", cpsPath));
- }
-
- /**
- * Has ancestor axis been populated.
- *
- * @return boolean value.
- */
- public boolean hasAncestorAxis() {
- return !(isEmpty(ancestorSchemaNodeIdentifier));
- }
-
- private void setParametersForSingleLeafValue(final String cpsPath, final Matcher matcher) {
- setCpsPathQueryType(CpsPathQueryType.XPATH_LEAF_VALUE);
- setXpathPrefix(matcher.group(1));
- setLeafName(matcher.group(2));
- setLeafValue(convertLeafValueToCorrectType(matcher.group(3), cpsPath));
- }
-
- private void setParametersForDescendantWithLeafValues(final String cpsPath, final Matcher matcher) {
- setCpsPathQueryType(CpsPathQueryType.XPATH_HAS_DESCENDANT_WITH_LEAF_VALUES);
- setDescendantName(matcher.group(1));
- final Map<String, Object> leafData = new HashMap<>();
- for (final String leafValuePair : matcher.group(2).split(INDIVIDUAL_LEAF_DETAIL_PATTERN)) {
- final var descendentMatcher = LEAF_VALUE_PATTERN.matcher(leafValuePair);
- if (descendentMatcher.matches()) {
- leafData.put(descendentMatcher.group(1),
- convertLeafValueToCorrectType(descendentMatcher.group(2), cpsPath));
- } else {
- throw new CpsPathException("Invalid cps path.",
- String.format("Cannot interpret or parse attributes in cps path '%s'.", cpsPath));
- }
- }
- setLeavesData(leafData);
- }
-
- private void setParametersForDescendantAnywhere(final Matcher matcher) {
- setCpsPathQueryType(CpsPathQueryType.XPATH_HAS_DESCENDANT_ANYWHERE);
- setDescendantName(matcher.group(1));
- }
-
- private static Object convertLeafValueToCorrectType(final String leafValueString, final String cpsPath) {
- final var stringValueWithSingleQuotesMatcher =
- LEAF_STRING_VALUE_IN_SINGLE_QUOTES_PATTERN.matcher(leafValueString);
- if (stringValueWithSingleQuotesMatcher.matches()) {
- return stringValueWithSingleQuotesMatcher.group(1);
- }
- final var stringValueWithDoubleQuotesMatcher =
- LEAF_STRING_VALUE_IN_DOUBLE_QUOTES_PATTERN.matcher(leafValueString);
- if (stringValueWithDoubleQuotesMatcher.matches()) {
- return stringValueWithDoubleQuotesMatcher.group(1);
- }
- final var integerValueMatcher = LEAF_INTEGER_VALUE_PATTERN.matcher(leafValueString);
- if (integerValueMatcher.matches()) {
- return Integer.valueOf(leafValueString);
- }
- throw new CpsPathException("Unsupported leaf value.",
- String.format("Unsupported leaf value '%s' in cps path '%s'.", leafValueString, cpsPath));
- }
-}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
index f64a22c89..b2477d47c 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
@@ -129,18 +129,6 @@ class CpsDataPersistenceQueryDataNodeSpec extends CpsPersistenceSpecBase {
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Cps Path query error scenario using descendant anywhere ends with yang list containing %scenario '() {
- when: 'a query is executed to get a data node by the given cps path'
- objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, OMIT_DESCENDANTS)
- then: 'exception is thrown'
- thrown(CpsPathException)
- where: 'the following data is used'
- scenario | cpsPath
- 'one of the leaf without value' | '//categories[@code=1 and @name=]'
- 'more than one leaf separated by or' | '//categories[@code=1 or @name="SciFi"]'
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
def 'Query for attribute by cps path of type ancestor with #scenario.'() {
when: 'the given cps path is parsed'
def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, INCLUDE_ALL_DESCENDANTS)
@@ -157,7 +145,15 @@ class CpsDataPersistenceQueryDataNodeSpec extends CpsPersistenceSpecBase {
'list with index value in the xpath prefix' | '//categories[@code=1]/book/ancestor::shop[@id=1]' || ['/shops/shop[@id=1]']
'ancestor with parent list' | '//book/ancestor::shop[@id=1]/categories[@code=2]' || ['/shops/shop[@id=1]/categories[@code=2]']
'ancestor with parent' | '//phonenumbers[@type="mob"]/ancestor::info/contact' || ['/shops/shop[@id=3]/info/contact']
- 'ancestor with parent that does not exist' | '//book/ancestor::/parentDoesNoExist/categories' || []
+ 'ancestor with parent that does not exist' | '//book/ancestor::parentDoesNoExist/categories' || []
'ancestor does not exist' | '//book/ancestor::ancestorDoesNotExist' || []
}
+
+ def 'Cps Path query with syntax error throws a CPS Path Exception.'() {
+ when: 'trying to execute a query with a syntax (parsing) error'
+ objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, 'cpsPath that cannot be parsed' , OMIT_DESCENDANTS)
+ then: 'exception is thrown'
+ thrown(CpsPathException)
+ }
+
}
diff --git a/cps-service/src/main/java/org/onap/cps/spi/exceptions/CpsPathException.java b/cps-service/src/main/java/org/onap/cps/spi/exceptions/CpsPathException.java
index fde5566b1..f1e8dc525 100644
--- a/cps-service/src/main/java/org/onap/cps/spi/exceptions/CpsPathException.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/exceptions/CpsPathException.java
@@ -23,13 +23,14 @@ public class CpsPathException extends CpsException {
private static final long serialVersionUID = 1006899957127327791L;
+ private static final String ERROR_MESSAGE = "Error while parsing cpsPath expression";
+
/**
* Constructor.
*
- * @param message the error message
* @param details the error details
*/
- public CpsPathException(final String message, final String details) {
- super(message, details);
+ public CpsPathException(final String details) {
+ super(ERROR_MESSAGE, details);
}
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy
index 8592af908..b0bd62905 100755
--- a/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy
@@ -115,8 +115,8 @@ class CpsExceptionsSpec extends Specification {
def 'Creating an exception that the schema set being used and cannot be deleted.'() {
expect: 'the exception details contains the correct message with dataspace and schema set names'
(new SchemaSetInUseException(dataspaceName, schemaSetName)).details
- == ("Schema Set with name ${schemaSetName} in dataspace ${dataspaceName} is having "
- + "Anchor records associated.")
+ == ("Schema Set with name ${schemaSetName} in dataspace ${dataspaceName} is having" +
+ " Anchor records associated.")
}
def 'Creating a exception that a datanode with a specified xpath does not exist.'() {
@@ -157,10 +157,8 @@ class CpsExceptionsSpec extends Specification {
def 'Creating a cps path exception.'() {
given: 'a cps path exception is created'
- def exception = new CpsPathException(providedMessage, providedDetails)
- expect: 'the exception has the provided message'
- exception.message == providedMessage
- and: 'the exception has the provided details'
+ def exception = new CpsPathException(providedDetails)
+ expect: 'the exception has the provided details'
exception.details == providedDetails
}
-} \ No newline at end of file
+}
diff --git a/pom.xml b/pom.xml
index f196cba61..b0f549b25 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,7 @@
<module>cps-rest</module>
<module>cps-ncmp-service</module>
<module>cps-ncmp-rest</module>
+ <module>cps-path-parser</module>
<module>cps-ri</module>
<module>checkstyle</module>
<module>spotbugs</module>
diff --git a/spotbugs/src/main/resources/spotbugs-exclude.xml b/spotbugs/src/main/resources/spotbugs-exclude.xml
index c46270c1c..7fdda7364 100644
--- a/spotbugs/src/main/resources/spotbugs-exclude.xml
+++ b/spotbugs/src/main/resources/spotbugs-exclude.xml
@@ -1,5 +1,9 @@
<FindBugsFilter>
<Match>
+ <!-- Ignore generated code from Antlr4 -->
+ <Package name="org.onap.cps.cpspath.parser.antlr4" />
+ </Match>
+ <Match>
<Or>
<!-- Anonymous inner classes are very common. -->
<Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON" />