summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToineSiebelink <toine.siebelink@est.tech>2020-10-01 14:43:49 +0100
committerToineSiebelink <toine.siebelink@est.tech>2020-10-02 12:16:54 +0100
commit79ade5d05ead5c020adcaa0220e42149ef18683d (patch)
tree050a69f9532a0ec11db2904f91e606b1897d7f5b
parentf244c9cff733fe756177f3d33f7624977b660f5f (diff)
introducing YangUtils with tests
Issue-ID: CCSDK-2757 Jira Link: https://jira.onap.org/browse/CCSDK-2757 Change-Id: I3c396ef1e29e9f30027702f3d36ee3bbb1de9b8e
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java28
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java99
-rw-r--r--cps/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy30
-rw-r--r--cps/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy80
-rw-r--r--cps/cps-service/src/test/java/org/onap/cps/TestUtils.java41
-rw-r--r--cps/cps-service/src/test/resources/bookstore.json34
-rw-r--r--cps/cps-service/src/test/resources/bookstore.yang50
-rw-r--r--cps/cps-service/src/test/resources/invalid.yang1
-rw-r--r--cps/cps-service/src/test/resources/someOtherFile.txt0
9 files changed, 333 insertions, 30 deletions
diff --git a/cps/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java b/cps/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java
index 8d6b63bd59..cb8e20c8a2 100644
--- a/cps/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java
+++ b/cps/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java
@@ -24,18 +24,14 @@ import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
-import java.util.Iterator;
-import java.util.ServiceLoader;
import org.onap.cps.api.CpService;
import org.onap.cps.spi.DataPersistencyService;
import org.onap.cps.spi.ModelPersistencyService;
+import org.onap.cps.utils.YangUtils;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
-import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -49,16 +45,7 @@ public class CpServiceImpl implements CpService {
private static final YangParserFactory PARSER_FACTORY;
- static {
- final Iterator<YangParserFactory> it =
- ServiceLoader.load(YangParserFactory.class).iterator();
- if (!it.hasNext()) {
- throw new IllegalStateException("No YangParserFactory found");
- }
- PARSER_FACTORY = it.next();
- }
-
- @Autowired
+ @Autowired
private ModelPersistencyService modelPersistencyService;
@Autowired
@@ -66,23 +53,18 @@ public class CpServiceImpl implements CpService {
@Override
- public final SchemaContext parseAndValidateModel(final String yangModelContent)
- throws IOException, YangParserException {
+ public final SchemaContext parseAndValidateModel(final String yangModelContent) throws IOException,
+ YangParserException {
final File tempFile = File.createTempFile("yang", ".yang");
try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) {
writer.write(yangModelContent);
- } catch (final IOException e) {
- LOGGER.error("Unable to write to temporary file {}", e.getMessage());
}
return parseAndValidateModel(tempFile);
}
@Override
public final SchemaContext parseAndValidateModel(final File yangModelFile) throws IOException, YangParserException {
- final YangTextSchemaSource yangTextSchemaSource = YangTextSchemaSource.forFile(yangModelFile);
- final YangParser yangParser = PARSER_FACTORY.createParser(StatementParserMode.DEFAULT_MODE);
- yangParser.addSource(yangTextSchemaSource);
- return yangParser.buildEffectiveModel();
+ return YangUtils.parseYangModelFile(yangModelFile);
}
@Override
diff --git a/cps/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java b/cps/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java
new file mode 100644
index 0000000000..e9757eca0d
--- /dev/null
+++ b/cps/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java
@@ -0,0 +1,99 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 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.utils;
+
+import com.google.gson.stream.JsonReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+public class YangUtils {
+
+ private static final YangParserFactory PARSER_FACTORY;
+
+ private YangUtils() {
+ throw new IllegalStateException("Utility class");
+ }
+
+ static {
+ final Iterator<YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
+ if (!it.hasNext()) {
+ throw new IllegalStateException("No YangParserFactory found");
+ }
+ PARSER_FACTORY = it.next();
+ }
+
+ /**
+ * Parse a file containing yang modules.
+ * @param yangModelFile a file containing one or more yang modules
+ * (please note the file has to have a .yang extension if not an exception will be thrown)
+ * @return a SchemaContext representing the yang model
+ * @throws IOException when the system as an IO issue
+ * @throws YangParserException when the file does not contain a valid yang structure
+ */
+ public static SchemaContext parseYangModelFile(final File yangModelFile) throws IOException, YangParserException {
+ YangTextSchemaSource yangTextSchemaSource = YangTextSchemaSource.forFile(yangModelFile);
+ final YangParser yangParser = PARSER_FACTORY
+ .createParser(StatementParserMode.DEFAULT_MODE);
+ yangParser.addSource(yangTextSchemaSource);
+ return yangParser.buildEffectiveModel();
+ }
+
+ /**
+ * Parse a file containing json data for a certain model (schemaContext).
+ * @param jsonData a string containing json data for the given model
+ * @param schemaContext the SchemaContext for the given data
+ * @return the NormalizedNode representing the json data
+ */
+ public static NormalizedNode<?, ?> parseJsonData(final String jsonData, final SchemaContext schemaContext)
+ throws IOException {
+ JSONCodecFactory jsonCodecFactory = JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02
+ .getShared(schemaContext);
+ final NormalizedNodeResult normalizedNodeResult = new NormalizedNodeResult();
+ final NormalizedNodeStreamWriter normalizedNodeStreamWriter = ImmutableNormalizedNodeStreamWriter
+ .from(normalizedNodeResult);
+ try (JsonParserStream jsonParserStream = JsonParserStream
+ .create(normalizedNodeStreamWriter, jsonCodecFactory)) {
+ final JsonReader jsonReader = new JsonReader(new StringReader(jsonData));
+ jsonParserStream.parse(jsonReader);
+ }
+ return normalizedNodeResult.getResult();
+ }
+
+ public static void chopNormalizedNode(NormalizedNode<?, ?> tree) {
+ //TODO Toine Siebelink, add code from proto-type (other user story)
+ }
+
+}
diff --git a/cps/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy b/cps/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy
index 27f7482112..a1c9dd951d 100644
--- a/cps/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy
+++ b/cps/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy
@@ -1,3 +1,22 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 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.api.impl
import org.onap.cps.spi.DataPersistencyService
@@ -14,14 +33,11 @@ class CpServiceImplSpec extends Specification {
objectUnderTest.dataPersistencyService = dataPersistencyService;
}
- def 'Storing a json object'() {
- given: 'that the data persistency service returns an id of 123'
+ def 'Cps Service provides to its client the id assigned by the system when storing a data structure'() {
+ given: 'that data persistency service is giving id 123 to a data structure it is asked to store'
dataPersistencyService.storeJsonStructure(_) >> 123
- when: 'a json structure is stored using the data persistency service'
- def result = objectUnderTest.storeJsonStructure('')
-
- then: ' the same id is returned'
- result == 123
+ expect: 'Cps service returns the same id when storing data structure'
+ objectUnderTest.storeJsonStructure('') == 123
}
}
diff --git a/cps/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy b/cps/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy
new file mode 100644
index 0000000000..8aabc48448
--- /dev/null
+++ b/cps/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy
@@ -0,0 +1,80 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 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.utils
+
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode
+import org.opendaylight.yangtools.yang.common.QName
+import org.opendaylight.yangtools.yang.common.Revision
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class YangUtilsSpec extends Specification{
+ def 'Parsing a valid Yang Model'() {
+ given: 'a yang model (file)'
+ def file = new File(ClassLoader.getSystemClassLoader().getResource('bookstore.yang').getFile())
+ when: 'the file is parsed'
+ def result = YangUtils.parseYangModelFile(file)
+ then: 'the result contain 1 module of the correct name and revision'
+ result.modules.size() == 1
+ def optionalModule = result.findModule('bookstore', Revision.of('2020-09-15'))
+ optionalModule.isPresent()
+ }
+
+ @Unroll
+ def 'parsing invalid yang file (#description)'() {
+ given: 'a file with #description'
+ File file = new File(ClassLoader.getSystemClassLoader().getResource(filename).getFile());
+ when: 'the file is parsed'
+ YangUtils.parseYangModelFile(file)
+ then: 'an exception is thrown'
+ thrown(expectedException)
+ where: 'the following parameters are used'
+ filename | description || expectedException
+ 'invalid.yang' | 'no valid content' || YangSyntaxErrorException
+ 'someOtherFile.txt' | 'no .yang extension' || IllegalArgumentException
+ }
+
+ def 'Parsing a valid Json String'() {
+ given: 'a yang model (file)'
+ def jsonData = org.onap.cps.TestUtils.getResourceFileContent('bookstore.json')
+ and: 'a model for that data'
+ def file = new File(ClassLoader.getSystemClassLoader().getResource('bookstore.yang').getFile())
+ def schemaContext = YangUtils.parseYangModelFile(file)
+ when: 'the json data is parsed'
+ NormalizedNode<?, ?> result = YangUtils.parseJsonData(jsonData, schemaContext);
+ then: 'the result is a normalized node of the correct type'
+ result.nodeType == QName.create('org:onap:ccsdk:sample','2020-09-15','bookstore')
+ }
+
+ def 'Parsing an invalid Json String'() {
+ given: 'a yang model (file)'
+ def jsonData = '{incomplete json'
+ and: 'a model'
+ def file = new File(ClassLoader.getSystemClassLoader().getResource('bookstore.yang').getFile())
+ def schemaContext = YangUtils.parseYangModelFile(file)
+ when: 'the invalid json is parsed'
+ YangUtils.parseJsonData(jsonData, schemaContext);
+ then: ' an exception is thrown'
+ thrown(IllegalStateException)
+ }
+
+
+}
diff --git a/cps/cps-service/src/test/java/org/onap/cps/TestUtils.java b/cps/cps-service/src/test/java/org/onap/cps/TestUtils.java
new file mode 100644
index 0000000000..e15cf525f9
--- /dev/null
+++ b/cps/cps-service/src/test/java/org/onap/cps/TestUtils.java
@@ -0,0 +1,41 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+/**
+ * Common convenience methods for testing
+ */
+public class TestUtils {
+ /**
+ * Convert a file in the test resource folder to a string
+ *
+ * @param filename to name of the file in test/resources
+ * @return the content of the file as a String
+ * @throws IOException when there is an IO issue
+ */
+ public static String getResourceFileContent(final String filename) throws IOException {
+ File file = new File(ClassLoader.getSystemClassLoader().getResource(filename).getFile());
+ return new String(Files.readAllBytes(file.toPath()));
+ }
+}
diff --git a/cps/cps-service/src/test/resources/bookstore.json b/cps/cps-service/src/test/resources/bookstore.json
new file mode 100644
index 0000000000..44d5d424cd
--- /dev/null
+++ b/cps/cps-service/src/test/resources/bookstore.json
@@ -0,0 +1,34 @@
+{
+ "test:bookstore":{
+ "categories":[
+ {
+ "name":"web",
+ "books":[
+ {
+ "authors":[
+ "Toine Siebelink","David Lang"
+ ],
+ "lang":"en",
+ "price":"123456",
+ "pub_year":"2020",
+ "title":"My first book"
+ }
+ ]
+ },
+ {
+ "name":"art",
+ "books":[
+ {
+ "authors":[
+ "Test"
+ ],
+ "lang":"en",
+ "price":"1234",
+ "pub_year":"2020",
+ "title":"My 2nd book"
+ }
+ ]
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/cps/cps-service/src/test/resources/bookstore.yang b/cps/cps-service/src/test/resources/bookstore.yang
new file mode 100644
index 0000000000..01eac5f339
--- /dev/null
+++ b/cps/cps-service/src/test/resources/bookstore.yang
@@ -0,0 +1,50 @@
+module bookstore {
+ yang-version 1.1;
+
+ namespace "org:onap:ccsdk:sample";
+
+ prefix book-store;
+
+ revision "2020-09-15" {
+ description
+ "Sample Model";
+ }
+
+ typedef year {
+ type uint16 {
+ range "1000..9999";
+ }
+ }
+
+ container bookstore {
+
+ list categories {
+
+ key name;
+
+ leaf name {
+ type string;
+ }
+
+ list books {
+ key title;
+
+ leaf title {
+ type string;
+ }
+ leaf lang {
+ type string;
+ }
+ leaf-list authors {
+ type string;
+ }
+ leaf pub_year {
+ type year;
+ }
+ leaf price {
+ type uint64;
+ }
+ }
+ }
+ }
+}
diff --git a/cps/cps-service/src/test/resources/invalid.yang b/cps/cps-service/src/test/resources/invalid.yang
new file mode 100644
index 0000000000..66cfd10796
--- /dev/null
+++ b/cps/cps-service/src/test/resources/invalid.yang
@@ -0,0 +1 @@
+no yang at all!
diff --git a/cps/cps-service/src/test/resources/someOtherFile.txt b/cps/cps-service/src/test/resources/someOtherFile.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/cps/cps-service/src/test/resources/someOtherFile.txt