diff options
Diffstat (limited to 'cps-service')
17 files changed, 978 insertions, 0 deletions
diff --git a/cps-service/pom.xml b/cps-service/pom.xml new file mode 100644 index 0000000000..3e8cc2debd --- /dev/null +++ b/cps-service/pom.xml @@ -0,0 +1,68 @@ +<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 https://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>0.0.1-SNAPSHOT</version>
+ <relativePath>../cps-parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>cps-service</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-util</artifactId>
+ </dependency>
+ <!-- required for processing yang data in json format -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-codec-gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ </dependency>
+ <dependency>
+ <!-- For logging -->
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <!-- For dependency injection -->
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <!-- For parsing JSON object -->
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</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>cglib</groupId>
+ <artifactId>cglib-nodep</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpService.java b/cps-service/src/main/java/org/onap/cps/api/CpService.java new file mode 100644 index 0000000000..4d94a46547 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/api/CpService.java @@ -0,0 +1,79 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation + * Modifications Copyright (C) 2020 Bell Canada. All rights reserved. + * ================================================================================ + * 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; + +import java.io.File; +import java.io.IOException; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.parser.api.YangParserException; + +/** + * Configuration and persistency service interface which holds methods for parsing and storing yang models and data. + */ +public interface CpService { + + /** + * Parse and validate a string representing a yang model to generate a schema context. + * + * @param yangModelContent the input stream + * @return the schema context + */ + SchemaContext parseAndValidateModel(final String yangModelContent); + + /** + * Parse and validate a file representing a yang model to generate a schema context. + * + * @param yangModelFile the yang file + * @return the schema context + */ + SchemaContext parseAndValidateModel(final File yangModelFile); + + /** + * Store schema context for a yang model. + * + * @param schemaContext the schema context + * @param dataspaceName the dataspace name + */ + void storeSchemaContext(final SchemaContext schemaContext, final String dataspaceName); + + /** + * Store the JSON structure in the database. + * + * @param jsonStructure the JSON structure. + * @return entity ID. + */ + Integer storeJsonStructure(final String jsonStructure); + + /** + * Read a JSON Object using the object identifier. + * + * @param jsonObjectId the JSON object identifier. + * @return the JSON structure. + */ + String getJsonById(final int jsonObjectId); + + /** + * Delete a JSON Object using the object identifier. + * + * @param jsonObjectId the JSON object identifier. + */ + void deleteJsonById(final int jsonObjectId); +} diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java new file mode 100644 index 0000000000..c33746e861 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java @@ -0,0 +1,100 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation + * Modifications Copyright (C) 2020 Bell Canada. All rights reserved. + * ================================================================================ + * 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 java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Optional; +import org.onap.cps.api.CpService; +import org.onap.cps.exceptions.CpsException; +import org.onap.cps.exceptions.CpsValidationException; +import org.onap.cps.spi.DataPersistencyService; +import org.onap.cps.spi.ModelPersistencyService; +import org.onap.cps.utils.YangUtils; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.parser.api.YangParserException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + + +@Component +public class CpServiceImpl implements CpService { + + @Autowired + private ModelPersistencyService modelPersistencyService; + + @Autowired + private DataPersistencyService dataPersistencyService; + + @Override + public final SchemaContext parseAndValidateModel(final String yangModelContent) { + + try { + final File tempFile = File.createTempFile("yang", ".yang"); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) { + writer.write(yangModelContent); + } + return parseAndValidateModel(tempFile); + } catch (IOException e) { + throw new CpsException(e); + } + } + + @Override + public final SchemaContext parseAndValidateModel(final File yangModelFile) { + try { + return YangUtils.parseYangModelFile(yangModelFile); + } catch (YangParserException e) { + throw new CpsValidationException("Yang file validation failed", e.getMessage()); + } catch (IOException e) { + throw new CpsException(e); + } + } + + @Override + public final Integer storeJsonStructure(final String jsonStructure) { + return dataPersistencyService.storeJsonStructure(jsonStructure); + } + + @Override + public final String getJsonById(final int jsonObjectId) { + return dataPersistencyService.getJsonById(jsonObjectId); + } + + @Override + public void deleteJsonById(int jsonObjectId) { + dataPersistencyService.deleteJsonById(jsonObjectId); + } + + @Override + public final void storeSchemaContext(final SchemaContext schemaContext, final String dataspaceName) { + for (final Module module : schemaContext.getModules()) { + Optional<Revision> optionalRevision = module.getRevision(); + String revisionValue = optionalRevision.isPresent() ? optionalRevision.get().toString() : null; + modelPersistencyService.storeModule(module.getNamespace().toString(), module.toString(), + revisionValue, dataspaceName); + } + } +} diff --git a/cps-service/src/main/java/org/onap/cps/exceptions/CpsException.java b/cps-service/src/main/java/org/onap/cps/exceptions/CpsException.java new file mode 100644 index 0000000000..b54453cd90 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/exceptions/CpsException.java @@ -0,0 +1,62 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * 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.exceptions; + +import lombok.Getter; +import org.springframework.core.NestedExceptionUtils; + +/** + * CP Service exception. + */ +public class CpsException extends RuntimeException { + + @Getter + String details; + + /** + * Constructor. + * + * @param cause the cause of the exception + */ + public CpsException(Throwable cause) { + super(cause.getMessage(), cause); + } + + /** + * Constructor. + * + * @param message the error message + * @param cause the cause of the exception + */ + public CpsException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor. + * + * @param message the error message + * @param details the error details + */ + public CpsException(String message, String details) { + super(message); + this.details = details; + } +} diff --git a/cps-service/src/main/java/org/onap/cps/exceptions/CpsNotFoundException.java b/cps-service/src/main/java/org/onap/cps/exceptions/CpsNotFoundException.java new file mode 100644 index 0000000000..f44fe806cf --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/exceptions/CpsNotFoundException.java @@ -0,0 +1,57 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * 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.exceptions; + +import lombok.Getter; + +/** + * CP Service exception. Indicates the requested data being absent. + */ +public class CpsNotFoundException extends CpsException { + + /** + * Constructor. + * + * @param cause the cause of the exception + */ + public CpsNotFoundException(Throwable cause) { + super(cause.getMessage(), cause); + } + + /** + * Constructor. + * + * @param message the error message + * @param cause the cause of the exception + */ + public CpsNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor. + * + * @param message the error message + * @param details the error details + */ + public CpsNotFoundException(String message, String details) { + super(message, details); + } +} diff --git a/cps-service/src/main/java/org/onap/cps/exceptions/CpsValidationException.java b/cps-service/src/main/java/org/onap/cps/exceptions/CpsValidationException.java new file mode 100644 index 0000000000..dba9c165b8 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/exceptions/CpsValidationException.java @@ -0,0 +1,55 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * 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.exceptions; + +/** + * CP Service exception. Indicates the parameter validation failure. + */ +public class CpsValidationException extends CpsException { + + /** + * Constructor. + * + * @param cause the cause of the exception + */ + public CpsValidationException(Throwable cause) { + super(cause.getMessage(), cause); + } + + /** + * Constructor. + * + * @param message the error message + * @param cause the cause of the exception + */ + public CpsValidationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor. + * + * @param message the error message + * @param details the error details + */ + public CpsValidationException(String message, String details) { + super(message, details); + } +} diff --git a/cps-service/src/main/java/org/onap/cps/spi/DataPersistencyService.java b/cps-service/src/main/java/org/onap/cps/spi/DataPersistencyService.java new file mode 100644 index 0000000000..ce51f40ac6 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/spi/DataPersistencyService.java @@ -0,0 +1,49 @@ +/* + * ============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.spi; + +/** + * Defines methods to access and manipulate data using the chosen database solution. + */ +public interface DataPersistencyService { + + /** + * Store the JSON structure in the database. + * + * @param jsonStructure the JSON structure. + * @return jsonEntityID the ID of the JSON entity. + */ + Integer storeJsonStructure(final String jsonStructure); + + /** + * Get the JSON structure from the database using the entity identifier. + * + * @param jsonStructureId the json entity identifier. + * @return a JSON Structure. + */ + String getJsonById(int jsonStructureId); + + /** + * Delete the JSON structure from the database using the entity identifier. + * + * @param jsonStructureId the json entity identifier. + */ + void deleteJsonById(int jsonStructureId); +}
\ No newline at end of file diff --git a/cps-service/src/main/java/org/onap/cps/spi/ModelPersistencyService.java b/cps-service/src/main/java/org/onap/cps/spi/ModelPersistencyService.java new file mode 100644 index 0000000000..2afdaa82a8 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/spi/ModelPersistencyService.java @@ -0,0 +1,39 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation + * Modifications Copyright (C) 2020 Bell Canada. All rights reserved. + * ================================================================================ + * 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; + +/** + * Defines methods to access and manipulate data using the chosen database solution. + */ +public interface ModelPersistencyService { + + /** + * Store the module from a yang model in the database. + * + * @param namespace module namespace + * @param moduleContent module content + * @param revision module revision + * @param dataspaceName the name of the dataspace the module is associated with + */ + void storeModule(final String namespace, final String moduleContent, final String revision, + final String dataspaceName); + +} 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 new file mode 100644 index 0000000000..e9757eca0d --- /dev/null +++ b/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-service/src/main/resources/logback-spring.xml b/cps-service/src/main/resources/logback-spring.xml new file mode 100644 index 0000000000..8a4ae07c5b --- /dev/null +++ b/cps-service/src/main/resources/logback-spring.xml @@ -0,0 +1,48 @@ +<configuration scan="true" debug="false"> + <include resource="org/springframework/boot/logging/logback/base.xml" /> + + <property name="queueSize" value="256" /> + <property name="maxFileSize" value="20MB" /> + <property name="maxHistory" value="30" /> + <property name="totalSizeCap" value="20MB" /> + + <!-- log file names --> + <property name="logName" value="cps" /> + + <property name="currentTimeStamp" value="%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",UTC}"/> + + <property name="debugPattern" + value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%thread|%X{RequestID}| %logger{50} - %msg%n" /> + + <appender name="Debug" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>../log/${logName}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logName}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>${maxFileSize}</maxFileSize> + <maxHistory>${maxHistory}</maxHistory> + <totalSizeCap>${totalSizeCap}</totalSizeCap> + </rollingPolicy> + <encoder> + <pattern>${debugPattern}</pattern> + </encoder> + </appender> + + <appender name="asyncDebug" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <appender-ref ref="Debug" /> + <includeCallerData>true</includeCallerData> + </appender> + + <logger name="org.onap.cps" level="DEBUG" additivity="false"> + <appender-ref ref="asyncDebug" /> + </logger> + + + <root level="INFO"> + <appender-ref ref="asyncDebug" /> + </root> + +</configuration>
\ No newline at end of file diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy new file mode 100644 index 0000000000..5f42810bd5 --- /dev/null +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy @@ -0,0 +1,116 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation + * Modifications Copyright (C) 2020 Bell Canada. All rights reserved. + * ================================================================================ + * 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.TestUtils +import org.onap.cps.exceptions.CpsValidationException +import org.onap.cps.spi.DataPersistencyService +import org.opendaylight.yangtools.yang.common.Revision +import org.opendaylight.yangtools.yang.model.api.SchemaContext +import spock.lang.Specification + +class CpServiceImplSpec extends Specification { + + def mockDataPersistencyService = Mock(DataPersistencyService) + def objectUnderTest = new CpServiceImpl() + + def setup() { + objectUnderTest.dataPersistencyService = mockDataPersistencyService + } + + 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' + mockDataPersistencyService.storeJsonStructure(_) >> 123 + expect: 'Cps service returns the same id when storing data structure' + objectUnderTest.storeJsonStructure('') == 123 + } + + def 'Parse and Validate a Yang Model with a Valid Yang Model'() { + given: 'a yang model (file)' + def yangModel = TestUtils.getResourceFileContent('bookstore.yang') + when: 'a valid model is parsed and validated' + def result = objectUnderTest.parseAndValidateModel(yangModel) + then: 'Verify a schema context for that model is created with the correct identity' + assertModule(result) + } + + def 'Parse and Validate a Yang Model Using a File'() { + given: 'a yang file that contains a yang model' + File file = new File(ClassLoader.getSystemClassLoader().getResource('bookstore.yang').getFile()) + when: 'a model is parsed and validated' + def result = objectUnderTest.parseAndValidateModel(file) + then: 'Verify a schema context for that model is created with the correct identity' + assertModule(result) + + } + + def assertModule(SchemaContext schemaContext){ + def optionalModule = schemaContext.findModule('bookstore', Revision.of('2020-09-15')) + return schemaContext.modules.size() == 1 && optionalModule.isPresent() + } + + def 'Parse and Validate an Invalid Model'() { + given: 'a yang file that contains a invalid yang model' + File file = new File(ClassLoader.getSystemClassLoader().getResource('invalid.yang').getFile()) + when: 'the model is parsed and validated' + objectUnderTest.parseAndValidateModel(file) + then: 'a CpsValidationException is thrown' + thrown(CpsValidationException) + } + + def 'Store a SchemaContext'() { + expect: 'No exception to be thrown when a valid model (schema) is stored' + objectUnderTest.storeSchemaContext(Stub(SchemaContext.class), "sampleDataspace") + } + + def 'Read a JSON object with a valid identifier'(){ + given: 'that the data persistence service returns a JSON structure for identifier 1' + mockDataPersistencyService.getJsonById(1) >> '{name : hello}' + expect: 'that the same JSON structure is returned by CPS' + objectUnderTest.getJsonById(1) == '{name : hello}' + } + + def 'Read a JSON object with an identifier that does not exist'(){ + given: 'that the data persistence service throws an exception' + def exceptionThrownByPersistenceService = new IllegalStateException() + mockDataPersistencyService.getJsonById(_) >> {throw exceptionThrownByPersistenceService} + when: 'we try to get the JSON structure' + objectUnderTest.getJsonById(1); + then: 'the same exception is thrown by CPS' + thrown(IllegalStateException) + } + + def 'Delete a JSON object with a valid identifier'(){ + given: 'that the data persistence service can delete a JSON structure for identifier 1' + mockDataPersistencyService.deleteJsonById(1) + expect: 'No exception is thrown when we delete a JSON structure with identifier 1' + objectUnderTest.deleteJsonById(1) + } + + def 'Delete a JSON object with an identifier that does not exist'(){ + given: 'that the data persistence service throws an exception' + mockDataPersistencyService.deleteJsonById(_) >> {throw new IllegalStateException()} + when: 'we try to delete a JSON structure' + objectUnderTest.deleteJsonById(100); + then: 'the same exception is thrown by CPS' + thrown(IllegalStateException) + } +}
\ No newline at end of file diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy new file mode 100644 index 0000000000..8aabc48448 --- /dev/null +++ b/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-service/src/test/java/org/onap/cps/TestUtils.java b/cps-service/src/test/java/org/onap/cps/TestUtils.java new file mode 100644 index 0000000000..07647520b0 --- /dev/null +++ b/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-service/src/test/resources/bookstore.json b/cps-service/src/test/resources/bookstore.json new file mode 100644 index 0000000000..44d5d424cd --- /dev/null +++ b/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-service/src/test/resources/bookstore.yang b/cps-service/src/test/resources/bookstore.yang new file mode 100644 index 0000000000..01eac5f339 --- /dev/null +++ b/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-service/src/test/resources/invalid.yang b/cps-service/src/test/resources/invalid.yang new file mode 100644 index 0000000000..66cfd10796 --- /dev/null +++ b/cps-service/src/test/resources/invalid.yang @@ -0,0 +1 @@ +no yang at all! diff --git a/cps-service/src/test/resources/someOtherFile.txt b/cps-service/src/test/resources/someOtherFile.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/cps-service/src/test/resources/someOtherFile.txt |