summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cps/.gitignore23
-rw-r--r--cps/Dockerfile1
-rw-r--r--cps/LICENSE.txt176
-rw-r--r--cps/README.md33
-rw-r--r--cps/cps-bom/pom.xml31
-rw-r--r--cps/cps-dependencies/pom.xml73
-rw-r--r--cps/cps-parent/pom.xml178
-rw-r--r--cps/cps-rest/docs/api/swagger/openapi.yml387
-rw-r--r--cps/cps-rest/pom.xml92
-rw-r--r--cps/cps-rest/src/main/java/org/onap/cps/Application.java32
-rw-r--r--cps/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestController.java182
-rw-r--r--cps/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java98
-rw-r--r--cps/cps-rest/src/main/java/org/onap/cps/rest/exceptions/ErrorMessage.java37
-rw-r--r--cps/cps-rest/src/main/java/org/onap/cps/swagger/config/SpringFoxConfig.java46
-rw-r--r--cps/cps-rest/src/main/resources/application.yml40
-rw-r--r--cps/cps-rest/src/main/resources/logback-spring.xml48
-rw-r--r--cps/cps-rest/src/main/resources/openapi-configuration.json28
-rw-r--r--cps/cps-ri/lombok.config2
-rw-r--r--cps/cps-ri/pom.xml40
-rw-r--r--cps/cps-ri/src/main/java/org/onap/cps/spi/entities/Dataspace.java62
-rw-r--r--cps/cps-ri/src/main/java/org/onap/cps/spi/entities/Fragment.java76
-rw-r--r--cps/cps-ri/src/main/java/org/onap/cps/spi/entities/JsonDataEntity.java54
-rw-r--r--cps/cps-ri/src/main/java/org/onap/cps/spi/entities/ModuleEntity.java84
-rw-r--r--cps/cps-ri/src/main/java/org/onap/cps/spi/impl/DataPersistencyServiceImpl.java69
-rw-r--r--cps/cps-ri/src/main/java/org/onap/cps/spi/impl/ModelPersistencyServiceImpl.java57
-rw-r--r--cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataRepository.java28
-rw-r--r--cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java32
-rw-r--r--cps/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleRepository.java29
-rw-r--r--cps/cps-ri/src/main/resources/hibernate.properties1
-rw-r--r--cps/cps-ri/src/main/resources/logback-spring.xml48
-rw-r--r--cps/cps-ri/src/main/resources/schema.sql63
-rw-r--r--cps/cps-service/pom.xml68
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/api/CpService.java79
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java100
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/exceptions/CpsException.java62
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/exceptions/CpsNotFoundException.java57
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/exceptions/CpsValidationException.java55
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/spi/DataPersistencyService.java49
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/spi/ModelPersistencyService.java39
-rw-r--r--cps/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java99
-rw-r--r--cps/cps-service/src/main/resources/logback-spring.xml48
-rw-r--r--cps/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy116
-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
-rw-r--r--cps/pom.xml37
-rw-r--r--cps/version.properties12
50 files changed, 3177 insertions, 0 deletions
diff --git a/cps/.gitignore b/cps/.gitignore
new file mode 100644
index 0000000000..624c9b63b9
--- /dev/null
+++ b/cps/.gitignore
@@ -0,0 +1,23 @@
+*.class
+*.jar
+*.war
+*.zip
+*.log
+
+target/
+log/
+
+.idea/
+.idea_modules/
+*.iml
+*.ipr
+*.iws
+
+.settings/
+bin/
+tmp/
+.metadata
+.classpath
+.project
+*.tmp
+.checkstyle \ No newline at end of file
diff --git a/cps/Dockerfile b/cps/Dockerfile
new file mode 100644
index 0000000000..81fdf16d37
--- /dev/null
+++ b/cps/Dockerfile
@@ -0,0 +1 @@
+FROM openjdk:11-jre-slim \ No newline at end of file
diff --git a/cps/LICENSE.txt b/cps/LICENSE.txt
new file mode 100644
index 0000000000..2bb9ad240f
--- /dev/null
+++ b/cps/LICENSE.txt
@@ -0,0 +1,176 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS \ No newline at end of file
diff --git a/cps/README.md b/cps/README.md
new file mode 100644
index 0000000000..d5f0c66f4c
--- /dev/null
+++ b/cps/README.md
@@ -0,0 +1,33 @@
+# Configuration Persistence Service
+
+This folder contains all files for
+[Configuration Persistence Service](https://wiki.onap.org/pages/viewpage.action?pageId=81406119).
+
+The code here is related to CPS POC, then it must be kept self contained in this cps folder to prevent any impact on
+current ccsdk components and to be ready to be moved in its own repo once CPS becomes a standalone project.
+
+
+## Running Locally
+
+* Run a postgres container instance and create `cpsdb' database:
+
+```
+CREATE USER cps WITH PASSWORD 'cps';
+CREATE DATABASE cpsdb OWNER cps;
+```
+
+* Build (from cps root folder)
+
+```bash
+mvn clean package
+```
+
+* Run (from cps root folder)
+
+```bash
+java -DDB_HOST=localhost -DDB_USERNAME=cps -DDB_PASSWORD=cps -jar cps-rest/target/cps-rest-0.0.1-SNAPSHOT.jar
+```
+
+* Browse
+ * [Swagger UI](http://localhost:8080/api/cps/swagger-ui/index.html)
+ * [Api Documentation](http://localhost:8080/api/cps/v3/api-docs)
diff --git a/cps/cps-bom/pom.xml b/cps/cps-bom/pom.xml
new file mode 100644
index 0000000000..ae9e806f3c
--- /dev/null
+++ b/cps/cps-bom/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-bom</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-service</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-rest</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-ri</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+</project> \ No newline at end of file
diff --git a/cps/cps-dependencies/pom.xml b/cps/cps-dependencies/pom.xml
new file mode 100644
index 0000000000..ee37b1e0a2
--- /dev/null
+++ b/cps/cps-dependencies/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-dependencies</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <name>${project.groupId}:${project.artifactId}</name>
+ <description>This artifact contains dependencyManagement declarations of upstream versions.</description>
+
+ <properties>
+ <groovy.version>3.0.6</groovy.version>
+ <hibernate-types.version>2.10.0</hibernate-types.version>
+ <spock-core.version>2.0-M2-groovy-3.0</spock-core.version>
+ <springboot.version>2.3.3.RELEASE</springboot.version>
+ <springfox.version>3.0.0</springfox.version>
+ <swagger.version>2.1.4</swagger.version>
+ <yangtools.version>5.0.6</yangtools.version>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-dependencies</artifactId>
+ <version>${springboot.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yangtools-artifacts</artifactId>
+ <version>${yangtools.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.swagger.core.v3</groupId>
+ <artifactId>swagger-annotations</artifactId>
+ <version>${swagger.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-boot-starter</artifactId>
+ <version>${springfox.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.vladmihalcea</groupId>
+ <artifactId>hibernate-types-52</artifactId>
+ <version>${hibernate-types.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy</artifactId>
+ <version>${groovy.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-core</artifactId>
+ <version>${spock-core.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib-nodep</artifactId>
+ <version>3.1</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+</project> \ No newline at end of file
diff --git a/cps/cps-parent/pom.xml b/cps/cps-parent/pom.xml
new file mode 100644
index 0000000000..0fe2e101a5
--- /dev/null
+++ b/cps/cps-parent/pom.xml
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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">
+ <parent>
+ <groupId>org.onap.oparent</groupId>
+ <artifactId>oparent</artifactId>
+ <version>3.1.0</version>
+ <relativePath/>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-parent</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <properties>
+ <java.version>11</java.version>
+ <oparent.version>3.1.0</oparent.version>
+ <spring-boot-maven-plugin.version>2.3.3.RELEASE</spring-boot-maven-plugin.version>
+ <swagger-codegen-maven-plugin.version>3.0.18</swagger-codegen-maven-plugin.version>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-dependencies</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-bom</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ <resource>
+ <directory>target/generated-sources/license</directory>
+ <includes>
+ <include>third-party-licenses.txt</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>target/generated-resources/licenses</directory>
+ <includes>
+ <include>*.*</include>
+ </includes>
+ <targetPath>third-party-licenses</targetPath>
+ </resource>
+ </resources>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>${spring-boot-maven-plugin.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>build-info</goal>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Swagger code generation. -->
+ <plugin>
+ <groupId>io.swagger.codegen.v3</groupId>
+ <artifactId>swagger-codegen-maven-plugin</artifactId>
+ <version>${swagger-codegen-maven-plugin.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ <configuration>
+ <inputSpec>${project.basedir}/docs/api/swagger/openapi.yml</inputSpec>
+ <invokerPackage>org.onap.cps.rest.controller</invokerPackage>
+ <modelPackage>org.onap.cps.rest.model</modelPackage>
+ <apiPackage>org.onap.cps.rest.api</apiPackage>
+ <language>spring</language>
+ <generateSupportingFiles>false</generateSupportingFiles>
+ <configOptions>
+ <sourceFolder>src/gen/java</sourceFolder>
+ <dateLibrary>java11</dateLibrary>
+ <interfaceOnly>true</interfaceOnly>
+ <useTags>true</useTags>
+ </configOptions>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${java.version}</source>
+ <target>${java.version}</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>onap-java-style</id>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ <phase>process-sources</phase>
+ <configuration>
+ <configLocation>onap-checkstyle/onap-java-style.xml</configLocation>
+ <sourceDirectories>${project.build.sourceDirectory}</sourceDirectories>
+ <includeResources>true</includeResources>
+ <includeTestSourceDirectory>true</includeTestSourceDirectory>
+ <includeTestResources>true</includeTestResources>
+ <consoleOutput>false</consoleOutput>
+ <violationSeverity>warning</violationSeverity>
+ <failOnViolation>true</failOnViolation>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.onap.oparent</groupId>
+ <artifactId>checkstyle</artifactId>
+ <version>${oparent.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <!-- Mandatory plugins for using Spock -->
+ <plugin>
+ <!-- The gmavenplus plugin is used to compile Groovy code.
+ To learn more about this plugin, visit https://github.com/groovy/GMavenPlus/wiki -->
+ <groupId>org.codehaus.gmavenplus</groupId>
+ <artifactId>gmavenplus-plugin</artifactId>
+ <version>1.9.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compileTests</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Required because names of spec classes don't match default
+ Surefire patterns (`*Test` etc.) -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>3.0.0-M5</version>
+ <configuration>
+ <useFile>false</useFile>
+ <includes>
+ <include>**/*Spec.java</include>
+ <include>**/*Test.java</include> <!-- Just in case of having also "normal" JUnit tests -->
+ </includes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project> \ No newline at end of file
diff --git a/cps/cps-rest/docs/api/swagger/openapi.yml b/cps/cps-rest/docs/api/swagger/openapi.yml
new file mode 100644
index 0000000000..9b2ac1ec5d
--- /dev/null
+++ b/cps/cps-rest/docs/api/swagger/openapi.yml
@@ -0,0 +1,387 @@
+openapi: 3.0.1
+info:
+ title: CPS API
+ description: Configuration Persistence Service API
+ version: "1.0"
+servers:
+ - url: //localhost:8088/
+tags:
+ - name: cps-rest
+ description: cps Resource
+paths:
+ /v1/dataspaces/{dataspace-name}/:
+ delete:
+ tags:
+ - cps-rest
+ summary: Delete the given dataspace
+ operationId: deleteDataspace
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 204:
+ description: No Content
+ content: {}
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ /v1/dataspaces/{dataspace-name}/anchors:
+ get:
+ tags:
+ - cps-rest
+ summary: Read all anchors, given a dataspace
+ operationId: getAnchors
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ 404:
+ description: Not Found
+ content: {}
+ post:
+ tags:
+ - cps-rest
+ summary: Create a new anchor in the given dataspace
+ operationId: createAnchor
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ required:
+ - file
+ properties:
+ multipartFile:
+ type: string
+ description: multipartFile
+ format: binary
+ required: true
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 201:
+ description: Created
+ content: {}
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ 404:
+ description: Not Found
+ content: {}
+ /v1/dataspaces/{dataspace-name}/anchors/{anchor-name}:
+ get:
+ tags:
+ - cps-rest
+ summary: Read an anchor given a anchor and a dataspace
+ operationId: getAnchor
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ - name: anchor-name
+ in: path
+ description: anchor-name
+ required: true
+ schema:
+ type: string
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ 404:
+ description: Not Found
+ content: {}
+ delete:
+ tags:
+ - cps-rest
+ summary: Delete an anchor given a anchor and a dataspace
+ operationId: deleteAnchor
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ - name: anchor-name
+ in: path
+ description: anchor-name
+ required: true
+ schema:
+ type: string
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 204:
+ description: No Content
+ content: {}
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ /v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/nodes:
+ get:
+ tags:
+ - cps-rest
+ summary: Get a node given an anchor for the given dataspace
+ operationId: getNodeByDataspaceAndAnchor
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ - name: anchor-name
+ in: path
+ description: anchor-name
+ required: true
+ schema:
+ type: string
+ requestBody:
+ description: xpath
+ content:
+ application/json:
+ schema:
+ type: string
+ required: true
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ 404:
+ description: Not Found
+ content: {}
+ x-codegen-request-body-name: xpath
+ /v1/dataspaces/{dataspace-name}/modules:
+ get:
+ tags:
+ - cps-rest
+ summary: Read all yang modules in the store
+ operationId: getModule
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ - name: namespace-name
+ in: query
+ description: namespace-name
+ schema:
+ type: string
+ - name: revision
+ in: query
+ description: revision
+ schema:
+ type: string
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ 404:
+ description: Not Found
+ content: {}
+ post:
+ tags:
+ - cps-rest
+ summary: Create modules for the given dataspace
+ operationId: createModules
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ required:
+ - file
+ properties:
+ multipartFile:
+ type: string
+ description: multipartFile
+ format: binary
+ required: true
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 201:
+ description: Created
+ content: {}
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ 404:
+ description: Not Found
+ content: {}
+ /v1/dataspaces/{dataspace-name}/nodes:
+ get:
+ tags:
+ - cps-rest
+ summary: Get all nodes for a given dataspace using an xpath or schema node identifier
+ operationId: getNode
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ requestBody:
+ description: requestBody
+ content:
+ application/json:
+ schema:
+ type: string
+ required: true
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ 404:
+ description: Not Found
+ content: {}
+ x-codegen-request-body-name: requestBody
+ post:
+ tags:
+ - cps-rest
+ summary: Create a node for a given anchor for the given dataspace
+ operationId: createNode
+ parameters:
+ - name: dataspace-name
+ in: path
+ description: dataspace-name
+ required: true
+ schema:
+ type: string
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ required:
+ - file
+ properties:
+ multipartFile:
+ type: string
+ description: multipartFile
+ format: binary
+ required: true
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 201:
+ description: Created
+ content: {}
+ 401:
+ description: Unauthorized
+ content: {}
+ 403:
+ description: Forbidden
+ content: {}
+ 404:
+ description: Not Found
+ content: {}
+components: {} \ No newline at end of file
diff --git a/cps/cps-rest/pom.xml b/cps/cps-rest/pom.xml
new file mode 100644
index 0000000000..4f8746a366
--- /dev/null
+++ b/cps/cps-rest/pom.xml
@@ -0,0 +1,92 @@
+<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-rest</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>cps-service</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>cps-ri</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-tomcat</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-jetty</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-actuator</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.swagger.core.v3</groupId>
+ <artifactId>swagger-annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-boot-starter</artifactId>
+ </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>
+ <!-- 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>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
+ <!-- Swagger code generation. -->
+ <plugin>
+ <groupId>io.swagger.codegen.v3</groupId>
+ <artifactId>swagger-codegen-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/cps/cps-rest/src/main/java/org/onap/cps/Application.java b/cps/cps-rest/src/main/java/org/onap/cps/Application.java
new file mode 100644
index 0000000000..9f6c2edce9
--- /dev/null
+++ b/cps/cps-rest/src/main/java/org/onap/cps/Application.java
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation. 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;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+} \ No newline at end of file
diff --git a/cps/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestController.java b/cps/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestController.java
new file mode 100644
index 0000000000..90c287cdaf
--- /dev/null
+++ b/cps/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestController.java
@@ -0,0 +1,182 @@
+/*
+ * ============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.rest.controller;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import javax.validation.Valid;
+import org.onap.cps.api.CpService;
+import org.onap.cps.exceptions.CpsException;
+import org.onap.cps.exceptions.CpsValidationException;
+import org.onap.cps.rest.api.CpsRestApi;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestController
+public class CpsRestController implements CpsRestApi {
+
+ @Autowired
+ private CpService cpService;
+
+ @Override
+ public ResponseEntity<Object> createAnchor(@Valid MultipartFile multipartFile, String dataspaceName) {
+ return null;
+ }
+
+ @Override
+ public ResponseEntity<Object> createModules(@Valid MultipartFile multipartFile, String dataspaceName) {
+ final File fileToParse = saveToFile(multipartFile);
+ final SchemaContext schemaContext = cpService.parseAndValidateModel(fileToParse);
+ cpService.storeSchemaContext(schemaContext, dataspaceName);
+ return new ResponseEntity<>("Resource successfully created", HttpStatus.CREATED);
+ }
+
+ @Override
+ public ResponseEntity<Object> createNode(@Valid MultipartFile multipartFile, String dataspaceName) {
+ return null;
+ }
+
+ @Override
+ public ResponseEntity<Object> deleteAnchor(String dataspaceName, String anchorName) {
+ return null;
+ }
+
+ @Override
+ public ResponseEntity<Object> deleteDataspace(String dataspaceName) {
+ return null;
+ }
+
+ @Override
+ public ResponseEntity<Object> getAnchor(String dataspaceName, String anchorName) {
+ return null;
+ }
+
+ @Override
+ public ResponseEntity<Object> getAnchors(String dataspaceName) {
+ return null;
+ }
+
+ @Override
+ public ResponseEntity<Object> getModule(String dataspaceName, @Valid String namespaceName, @Valid String revision) {
+ return null;
+ }
+
+ @Override
+ public ResponseEntity<Object> getNode(@Valid String body, String dataspaceName) {
+ return null;
+ }
+
+ @Override
+ public ResponseEntity<Object> getNodeByDataspaceAndAnchor(@Valid String body, String dataspaceName,
+ String anchorName) {
+ return null;
+ }
+
+ /*
+ Old rest endpoints before contract first approach (Need to be removed).
+ */
+
+ /**
+ * Upload a JSON file.
+ *
+ * @param uploadedFile the JSON Multipart file.
+ * @return a ResponseEntity.
+ */
+ @PostMapping("/upload-yang-json-data-file")
+ public final ResponseEntity<String> uploadYangJsonDataFile(
+ @RequestParam("file") MultipartFile uploadedFile) {
+ validateJsonStructure(uploadedFile);
+ final int persistenceObjectId = cpService.storeJsonStructure(getJsonString(uploadedFile));
+ return new ResponseEntity<String>(
+ "Object stored in CPS with identity: " + persistenceObjectId, HttpStatus.OK);
+
+ }
+
+ /**
+ * Read a JSON Object using the object identifier.
+ *
+ * @param jsonObjectId the JSON object identifier.
+ * @return a ResponseEntity.
+ */
+ @GetMapping("/json-object/{id}")
+ public final ResponseEntity<String> getJsonObjectById(
+ @PathVariable("id") final int jsonObjectId) {
+ return new ResponseEntity<String>(cpService.getJsonById(jsonObjectId), HttpStatus.OK);
+ }
+
+ /**
+ * Delete a JSON Object using the object identifier.
+ *
+ * @param jsonObjectId the JSON object identifier.
+ * @return a ResponseEntity.
+ */
+ @DeleteMapping("json-object/{id}")
+ public final ResponseEntity<Object> deleteJsonObjectById(
+ @PathVariable("id") final int jsonObjectId) {
+ cpService.deleteJsonById(jsonObjectId);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+
+ private static void validateJsonStructure(final MultipartFile multipartFile) {
+ try {
+ final Gson gson = new Gson();
+ gson.fromJson(getJsonString(multipartFile), Object.class);
+ } catch (JsonSyntaxException e) {
+ throw new CpsValidationException("Not a valid JSON file.", e);
+ }
+ }
+
+ private static File saveToFile(final MultipartFile multipartFile) {
+ try {
+ final File file = File.createTempFile("tempFile", ".yang");
+ file.deleteOnExit();
+
+ try (OutputStream outputStream = new FileOutputStream(file)) {
+ outputStream.write(multipartFile.getBytes());
+ }
+ return file;
+
+ } catch (IOException e) {
+ throw new CpsException(e);
+ }
+ }
+
+ private static String getJsonString(final MultipartFile multipartFile) {
+ try {
+ return new String(multipartFile.getBytes());
+ } catch (IOException e) {
+ throw new CpsException(e);
+ }
+ }
+} \ No newline at end of file
diff --git a/cps/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java b/cps/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java
new file mode 100644
index 0000000000..9cf4935d4e
--- /dev/null
+++ b/cps/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java
@@ -0,0 +1,98 @@
+/*
+ * ============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.rest.exceptions;
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.hibernate.exception.ConstraintViolationException;
+import org.onap.cps.exceptions.CpsException;
+import org.onap.cps.exceptions.CpsNotFoundException;
+import org.onap.cps.exceptions.CpsValidationException;
+import org.onap.cps.rest.controller.CpsRestController;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice(assignableTypes = {CpsRestController.class})
+public class CpsRestExceptionHandler {
+
+ /**
+ * Default exception handler.
+ *
+ * @param exception the exception to handle
+ * @return response with response code 500.
+ */
+ @ExceptionHandler
+ public ResponseEntity<Object> handleInternalErrorException(Exception exception) {
+ return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception);
+ }
+
+ /*
+ TODO: Rid off extra dependencies.
+
+ Generic exception handler and CpsException (incl. children) are the only
+ exceptions to be handled here. All the other exceptions which require a special
+ treatment should be rethrown as CpsException in the place of occurrence ->
+ e.g. persistence exceptions are to be handled in cps-ri module.
+ */
+
+ @ExceptionHandler({ConstraintViolationException.class})
+ public ResponseEntity<Object> handleBadRequestException(Exception exception) {
+ return buildErrorResponse(HttpStatus.BAD_REQUEST, exception);
+ }
+
+ @ExceptionHandler({EmptyResultDataAccessException.class})
+ public ResponseEntity<Object> handleNotFoundException(Exception exception) {
+ return buildErrorResponse(HttpStatus.NOT_FOUND, exception);
+ }
+
+ @ExceptionHandler({CpsException.class})
+ public ResponseEntity<Object> handleCpsException(CpsException exception) {
+ return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception.getMessage(), extractDetails(exception));
+ }
+
+ @ExceptionHandler({CpsValidationException.class})
+ public ResponseEntity<Object> handleCpsValidationException(CpsException exception) {
+ return buildErrorResponse(HttpStatus.BAD_REQUEST, exception.getMessage(), extractDetails(exception));
+ }
+
+ @ExceptionHandler({CpsNotFoundException.class})
+ public ResponseEntity<Object> handleCpsNotFoundException(CpsException exception) {
+ return buildErrorResponse(HttpStatus.NOT_FOUND, exception.getMessage(), extractDetails(exception));
+ }
+
+ private static ResponseEntity<Object> buildErrorResponse(HttpStatus status, Exception exception) {
+ return buildErrorResponse(status, exception.getMessage(), ExceptionUtils.getStackTrace(exception));
+ }
+
+ private static ResponseEntity<Object> buildErrorResponse(HttpStatus status, String message, String details) {
+ return new ResponseEntity<>(
+ ErrorMessage.builder().status(status.toString()).message(message).details(details).build(),
+ status
+ );
+ }
+
+ private static String extractDetails(CpsException exception) {
+ return exception.getCause() == null
+ ? exception.getDetails()
+ : ExceptionUtils.getStackTrace(exception.getCause());
+ }
+}
diff --git a/cps/cps-rest/src/main/java/org/onap/cps/rest/exceptions/ErrorMessage.java b/cps/cps-rest/src/main/java/org/onap/cps/rest/exceptions/ErrorMessage.java
new file mode 100644
index 0000000000..1f2a0b7860
--- /dev/null
+++ b/cps/cps-rest/src/main/java/org/onap/cps/rest/exceptions/ErrorMessage.java
@@ -0,0 +1,37 @@
+/*
+ * ============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.rest.exceptions;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * Temporary POJO class used as error response model.
+ * TODO: To replace with model class generated from Open API specification.
+ *
+ */
+@Builder
+@Data
+public class ErrorMessage {
+ private String status;
+ private String message;
+ private String details;
+}
+
diff --git a/cps/cps-rest/src/main/java/org/onap/cps/swagger/config/SpringFoxConfig.java b/cps/cps-rest/src/main/java/org/onap/cps/swagger/config/SpringFoxConfig.java
new file mode 100644
index 0000000000..73e179511b
--- /dev/null
+++ b/cps/cps-rest/src/main/java/org/onap/cps/swagger/config/SpringFoxConfig.java
@@ -0,0 +1,46 @@
+/*
+ * ============LICENSE_START=======================================================
+ * 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.swagger.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+
+/**
+ * Swagger configuration.
+ */
+@Configuration
+public class SpringFoxConfig {
+
+ /**
+ * Define api configuration.
+ */
+ @Bean
+ public Docket api() {
+ return new Docket(DocumentationType.OAS_30)
+ .select()
+ .apis(RequestHandlerSelectors.any())
+ .paths(PathSelectors.any())
+ .build();
+ }
+}
diff --git a/cps/cps-rest/src/main/resources/application.yml b/cps/cps-rest/src/main/resources/application.yml
new file mode 100644
index 0000000000..e8af0bcfb2
--- /dev/null
+++ b/cps/cps-rest/src/main/resources/application.yml
@@ -0,0 +1,40 @@
+server:
+ port: 8080
+ servlet:
+ context-path: /api/cps
+
+spring:
+ main:
+ banner-mode: "off"
+# for POC only, later this should move to cpi-ri module
+ jpa:
+ ddl-auto: create
+ open-in-view: false
+ properties:
+ hibernate:
+ enable_lazy_load_no_trans: true
+ dialect: org.hibernate.dialect.PostgreSQLDialect
+
+ datasource:
+ url: jdbc:postgresql://${DB_HOST}:5432/cpsdb
+ username: ${DB_USERNAME}
+ password: ${DB_PASSWORD}
+ driverClassName: org.postgresql.Driver
+ initialization-mode: always
+
+# Actuator
+management:
+ endpoints:
+ web:
+ base-path: /manage
+ endpoint:
+ health:
+ show-details: always
+ # kubernetes probes: liveness and readiness
+ probes:
+ enabled: true
+
+logging:
+ level:
+ org:
+ springframework: INFO
diff --git a/cps/cps-rest/src/main/resources/logback-spring.xml b/cps/cps-rest/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000000..8a4ae07c5b
--- /dev/null
+++ b/cps/cps-rest/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{&quot;yyyy-MM-dd'T'HH:mm:ss.SSSXXX&quot;,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/cps-rest/src/main/resources/openapi-configuration.json b/cps/cps-rest/src/main/resources/openapi-configuration.json
new file mode 100644
index 0000000000..ad5998feb7
--- /dev/null
+++ b/cps/cps-rest/src/main/resources/openapi-configuration.json
@@ -0,0 +1,28 @@
+{
+ "resourcePackages": [
+ "org.onap.cps.rest.controller"
+ ],
+ "prettyPrint": true,
+ "cacheTTL": 0,
+ "openAPI": {
+ "info": {
+ "title": "ONAP Open API v3 CPS Spec",
+ "description": "The API Description may be multiline, and GitHub Flavored Markdown, GFM syntax, can be used for rich text representation.",
+ "x-logo": {
+ "url": "logo.png"
+ },
+ "contact": {
+ "name": "ONAP",
+ "url": "https://onap.readthedocs.io",
+ "email": "onap-discuss@lists.onap.org"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0"
+ },
+ "version": "1.2.34",
+ "x-planned-retirement-date": "202207",
+ "x-component": "Modeling"
+ }
+ }
+}
diff --git a/cps/cps-ri/lombok.config b/cps/cps-ri/lombok.config
new file mode 100644
index 0000000000..a23edb413f
--- /dev/null
+++ b/cps/cps-ri/lombok.config
@@ -0,0 +1,2 @@
+config.stopBubbling = true
+lombok.addLombokGeneratedAnnotation = true \ No newline at end of file
diff --git a/cps/cps-ri/pom.xml b/cps/cps-ri/pom.xml
new file mode 100644
index 0000000000..2c28212afa
--- /dev/null
+++ b/cps/cps-ri/pom.xml
@@ -0,0 +1,40 @@
+<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-ri</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>cps-service</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-data-jpa</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-validation</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ </dependency>
+ <!-- Add Hibernate support for Postgres datatype JSONB -->
+ <dependency>
+ <groupId>com.vladmihalcea</groupId>
+ <artifactId>hibernate-types-52</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/Dataspace.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/Dataspace.java
new file mode 100644
index 0000000000..627a14467d
--- /dev/null
+++ b/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/Dataspace.java
@@ -0,0 +1,62 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation. 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.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+
+/**
+ * Entity to store a dataspace.
+ */
+@Getter
+@Setter
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Table(name = "dataspace")
+public class Dataspace {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Integer id;
+
+ @NotNull
+ @Column(columnDefinition = "text")
+ private String name;
+
+ /**
+ * Initialize a Dataspace .
+ *
+ * @param name the Dataspace name.
+ */
+ public Dataspace(String name) {
+ this.name = name;
+ }
+} \ No newline at end of file
diff --git a/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/Fragment.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/Fragment.java
new file mode 100644
index 0000000000..12422dc5f6
--- /dev/null
+++ b/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/Fragment.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation. 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.entities;
+
+import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToOne;
+import javax.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.hibernate.annotations.Type;
+import org.hibernate.annotations.TypeDef;
+import org.hibernate.annotations.TypeDefs;
+
+/**
+ * Entity to store a fragment.
+ */
+@Getter
+@Setter
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)})
+public class Fragment {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotNull
+ @Column(columnDefinition = "text")
+ private String xpath;
+
+ @Type(type = "jsonb")
+ @Column(columnDefinition = "jsonb")
+ private String attributes;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "dataspace_id")
+ private Dataspace dataspace;
+
+ @OneToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "anchor_id")
+ private Fragment anchorFragment;
+
+ @OneToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "parent_id")
+ private Fragment parentFragment;
+}
diff --git a/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/JsonDataEntity.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/JsonDataEntity.java
new file mode 100644
index 0000000000..413362e384
--- /dev/null
+++ b/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/JsonDataEntity.java
@@ -0,0 +1,54 @@
+/*
+ * ============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.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * Entity to store a JSON data structure.
+ */
+@Getter
+@Setter
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Table(name = "JsonData")
+public class JsonDataEntity {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Integer id;
+
+ @Column
+ private String jsonStructure;
+
+ public JsonDataEntity(String jsonStructure) {
+ this.jsonStructure = jsonStructure;
+ }
+}
diff --git a/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/ModuleEntity.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/ModuleEntity.java
new file mode 100644
index 0000000000..d2130aeec1
--- /dev/null
+++ b/cps/cps-ri/src/main/java/org/onap/cps/spi/entities/ModuleEntity.java
@@ -0,0 +1,84 @@
+/*
+ * ============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.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+
+/**
+ * Entity to store a yang module.
+ */
+@Getter
+@Setter
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Table(name = "module")
+public class ModuleEntity {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Integer id;
+
+ @NotNull
+ @Column
+ private String moduleContent;
+
+ @NotNull
+ @Column
+ private String revision;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "dataspace_id", referencedColumnName = "ID")
+ private Dataspace dataspace;
+
+ @NotNull
+ @Column
+ private String namespace;
+
+ /**
+ * Initialize a module entity.
+ *
+ * @param namespace the module namespace.
+ * @param moduleContent the module content.
+ * @param revision the revision number of the module.
+ * @param dataspace the dataspace related to the module.
+ */
+ public ModuleEntity(String namespace, String moduleContent, String revision, Dataspace dataspace) {
+ this.namespace = namespace;
+ this.moduleContent = moduleContent;
+ this.revision = revision;
+ this.dataspace = dataspace;
+ }
+} \ No newline at end of file
diff --git a/cps/cps-ri/src/main/java/org/onap/cps/spi/impl/DataPersistencyServiceImpl.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/impl/DataPersistencyServiceImpl.java
new file mode 100644
index 0000000000..2b4f1357de
--- /dev/null
+++ b/cps/cps-ri/src/main/java/org/onap/cps/spi/impl/DataPersistencyServiceImpl.java
@@ -0,0 +1,69 @@
+/*
+ * ============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.impl;
+
+import org.onap.cps.spi.DataPersistencyService;
+import org.onap.cps.spi.entities.JsonDataEntity;
+import org.onap.cps.spi.repository.DataRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+@Component
+public class DataPersistencyServiceImpl implements DataPersistencyService {
+
+ @Autowired
+ private DataRepository dataRepository;
+
+ /**
+ * Method to store a JSON data structure in the database.
+ *
+ * @param jsonStructure the JSON data structure.
+ * @return the entity identifier.
+ */
+ @Override
+ public final Integer storeJsonStructure(final String jsonStructure) {
+ final JsonDataEntity jsonDataEntity = new JsonDataEntity(jsonStructure);
+ dataRepository.save(jsonDataEntity);
+ return jsonDataEntity.getId();
+ }
+
+ /*
+ * Return the JSON structure from the database using the object identifier.
+ *
+ * @param jsonStructureId the JSON object identifier.
+ *
+ * @return the JSON structure from the database as a string.
+ */
+ @Override
+ public final String getJsonById(final int jsonStructureId) {
+ return dataRepository.getOne(jsonStructureId).getJsonStructure();
+ }
+
+ /**
+ * Delete the JSON structure from the database using the object identifier.
+ *
+ * @param jsonStructureId the JSON object identifier.
+ */
+ @Override
+ public void deleteJsonById(int jsonStructureId) {
+ dataRepository.deleteById(jsonStructureId);
+ }
+}
diff --git a/cps/cps-ri/src/main/java/org/onap/cps/spi/impl/ModelPersistencyServiceImpl.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/impl/ModelPersistencyServiceImpl.java
new file mode 100644
index 0000000000..01c7a7b53b
--- /dev/null
+++ b/cps/cps-ri/src/main/java/org/onap/cps/spi/impl/ModelPersistencyServiceImpl.java
@@ -0,0 +1,57 @@
+/*
+ * ============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.impl;
+
+import org.onap.cps.spi.ModelPersistencyService;
+import org.onap.cps.spi.entities.Dataspace;
+import org.onap.cps.spi.entities.ModuleEntity;
+import org.onap.cps.spi.repository.DataspaceRepository;
+import org.onap.cps.spi.repository.ModuleRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ModelPersistencyServiceImpl implements ModelPersistencyService {
+
+
+ private final ModuleRepository moduleRepository;
+
+ private final DataspaceRepository dataspaceRepository;
+
+ @Autowired
+ public ModelPersistencyServiceImpl(final ModuleRepository moduleRepository,
+ final DataspaceRepository dataspaceRepository) {
+ this.moduleRepository = moduleRepository;
+ this.dataspaceRepository = dataspaceRepository;
+ }
+
+ @Override
+ public void storeModule(final String namespace, final String moduleContent, final String revision,
+ final String dataspaceName) {
+ final Dataspace dataspace = new Dataspace(dataspaceName);
+ if (Boolean.FALSE.equals(dataspaceRepository.existsByName(dataspaceName))) {
+ dataspaceRepository.save(dataspace);
+ }
+ dataspace.setId(dataspaceRepository.findByName(dataspaceName).getId());
+ final ModuleEntity moduleEntity = new ModuleEntity(namespace, moduleContent, revision, dataspace);
+ moduleRepository.save(moduleEntity);
+ }
+}
diff --git a/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataRepository.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataRepository.java
new file mode 100644
index 0000000000..f3dd586007
--- /dev/null
+++ b/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataRepository.java
@@ -0,0 +1,28 @@
+/*
+ * ============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.repository;
+
+import org.onap.cps.spi.entities.JsonDataEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface DataRepository extends JpaRepository<JsonDataEntity, Integer> {
+} \ No newline at end of file
diff --git a/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java
new file mode 100644
index 0000000000..46a5266103
--- /dev/null
+++ b/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java
@@ -0,0 +1,32 @@
+/*
+ * ============LICENSE_START=======================================================
+ * 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.repository;
+
+
+import org.onap.cps.spi.entities.Dataspace;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface DataspaceRepository extends JpaRepository<Dataspace, Integer> {
+ Boolean existsByName(String name); //Checks if there are any records by name()
+
+ Dataspace findByName(String name);
+} \ No newline at end of file
diff --git a/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleRepository.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleRepository.java
new file mode 100644
index 0000000000..f9078d7c14
--- /dev/null
+++ b/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleRepository.java
@@ -0,0 +1,29 @@
+/*
+ * ============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.repository;
+
+
+import org.onap.cps.spi.entities.ModuleEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ModuleRepository extends JpaRepository<ModuleEntity, Integer> {
+} \ No newline at end of file
diff --git a/cps/cps-ri/src/main/resources/hibernate.properties b/cps/cps-ri/src/main/resources/hibernate.properties
new file mode 100644
index 0000000000..a22fe63cfa
--- /dev/null
+++ b/cps/cps-ri/src/main/resources/hibernate.properties
@@ -0,0 +1 @@
+hibernate.types.print.banner=false \ No newline at end of file
diff --git a/cps/cps-ri/src/main/resources/logback-spring.xml b/cps/cps-ri/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000000..8a4ae07c5b
--- /dev/null
+++ b/cps/cps-ri/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{&quot;yyyy-MM-dd'T'HH:mm:ss.SSSXXX&quot;,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/cps-ri/src/main/resources/schema.sql b/cps/cps-ri/src/main/resources/schema.sql
new file mode 100644
index 0000000000..6a76fbd191
--- /dev/null
+++ b/cps/cps-ri/src/main/resources/schema.sql
@@ -0,0 +1,63 @@
+CREATE TABLE IF NOT EXISTS RELATION_TYPE
+(
+ RELATION_TYPE TEXT NOT NULL,
+ ID SERIAL PRIMARY KEY
+);
+
+CREATE TABLE IF NOT EXISTS DATASPACE
+(
+ ID SERIAL PRIMARY KEY,
+ NAME TEXT NOT NULL,
+ CONSTRAINT "UQ_NAME" UNIQUE (NAME)
+);
+
+CREATE TABLE IF NOT EXISTS SCHEMA_NODE
+(
+ SCHEMA_NODE_IDENTIFIER TEXT NOT NULL,
+ ID SERIAL PRIMARY KEY
+);
+
+CREATE TABLE IF NOT EXISTS FRAGMENT
+(
+ ID BIGSERIAL PRIMARY KEY,
+ XPATH TEXT NOT NULL,
+ DATASPACE_ID INTEGER NOT NULL REFERENCES DATASPACE(ID),
+ ATTRIBUTES JSONB,
+ ANCHOR_ID BIGINT REFERENCES FRAGMENT(ID),
+ PARENT_ID BIGINT REFERENCES FRAGMENT(ID),
+ MODULE_SET_ID INTEGER REFERENCES MODULE_SET(ID),
+ SCHEMA_NODE_ID INTEGER REFERENCES SCHEMA_NODE(ID)
+);
+
+CREATE TABLE IF NOT EXISTS RELATION
+(
+ FROM_FRAGMENT_ID BIGINT NOT NULL REFERENCES FRAGMENT(ID),
+ TO_FRAGMENT_ID BIGINT NOT NULL REFERENCES FRAGMENT(ID),
+ RELATION_TYPE_ID INTEGER NOT NULL REFERENCES RELATION_TYPE(ID),
+ FROM_REL_XPATH TEXT NOT NULL,
+ TO_REL_XPATH TEXT NOT NULL,
+ CONSTRAINT RELATION_PKEY PRIMARY KEY (TO_FRAGMENT_ID, FROM_FRAGMENT_ID, RELATION_TYPE_ID)
+);
+
+CREATE TABLE IF NOT EXISTS MODULE
+(
+ NAMESPACE TEXT NOT NULL,
+ REVISION TEXT NOT NULL,
+ MODULE_CONTENT TEXT NOT NULL,
+ DATASPACE_ID BIGINT NOT NULL,
+ ID SERIAL PRIMARY KEY,
+ UNIQUE (NAMESPACE, REVISION),
+ CONSTRAINT module_dataspace FOREIGN KEY (DATASPACE_ID) REFERENCES DATASPACE (id) ON UPDATE CASCADE ON DELETE CASCADE
+);
+
+CREATE INDEX IF NOT EXISTS "FKI_FRAGMENT_DATASPACE_ID_FK" ON FRAGMENT USING BTREE(DATASPACE_ID) ;
+CREATE INDEX IF NOT EXISTS "FKI_FRAGMENT_MODULE_SET_ID_FK" ON FRAGMENT USING BTREE(MODULE_SET_ID) ;
+CREATE INDEX IF NOT EXISTS "FKI_FRAGMENT_PARENT_ID_FK" ON FRAGMENT USING BTREE(PARENT_ID) ;
+CREATE INDEX IF NOT EXISTS "FKI_FRAGMENT_ANCHOR_ID_FK" ON FRAGMENT USING BTREE(ANCHOR_ID) ;
+CREATE INDEX IF NOT EXISTS "PERF_SCHEMA_NODE_SCHEMA_NODE_ID" ON SCHEMA_NODE USING BTREE(SCHEMA_NODE_IDENTIFIER) ;
+CREATE INDEX IF NOT EXISTS "FKI_SCHEMA_NODE_ID_TO_ID" ON FRAGMENT USING BTREE(SCHEMA_NODE_ID) ;
+CREATE INDEX IF NOT EXISTS "FKI_RELATION_TYPE_ID_FK" ON RELATION USING BTREE(RELATION_TYPE_ID);
+CREATE INDEX IF NOT EXISTS "FKI_RELATIONS_FROM_ID_FK" ON RELATION USING BTREE(FROM_FRAGMENT_ID);
+CREATE INDEX IF NOT EXISTS "FKI_RELATIONS_TO_ID_FK" ON RELATION USING BTREE(TO_FRAGMENT_ID);
+CREATE INDEX IF NOT EXISTS "PERF_MODULE_SET_MODULE_SET_REFERENCE" ON MODULE_SET USING BTREE(MODULE_SET_REFERENCE) ;
+CREATE UNIQUE INDEX IF NOT EXISTS "UQ_FRAGMENT_XPATH"ON FRAGMENT USING btree(xpath COLLATE pg_catalog."default" text_pattern_ops, dataspace_id); \ No newline at end of file
diff --git a/cps/cps-service/pom.xml b/cps/cps-service/pom.xml
new file mode 100644
index 0000000000..3e8cc2debd
--- /dev/null
+++ b/cps/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/cps-service/src/main/java/org/onap/cps/api/CpService.java b/cps/cps-service/src/main/java/org/onap/cps/api/CpService.java
new file mode 100644
index 0000000000..4d94a46547
--- /dev/null
+++ b/cps/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/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
new file mode 100644
index 0000000000..c33746e861
--- /dev/null
+++ b/cps/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/cps-service/src/main/java/org/onap/cps/exceptions/CpsException.java b/cps/cps-service/src/main/java/org/onap/cps/exceptions/CpsException.java
new file mode 100644
index 0000000000..b54453cd90
--- /dev/null
+++ b/cps/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/cps-service/src/main/java/org/onap/cps/exceptions/CpsNotFoundException.java b/cps/cps-service/src/main/java/org/onap/cps/exceptions/CpsNotFoundException.java
new file mode 100644
index 0000000000..f44fe806cf
--- /dev/null
+++ b/cps/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/cps-service/src/main/java/org/onap/cps/exceptions/CpsValidationException.java b/cps/cps-service/src/main/java/org/onap/cps/exceptions/CpsValidationException.java
new file mode 100644
index 0000000000..dba9c165b8
--- /dev/null
+++ b/cps/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/cps-service/src/main/java/org/onap/cps/spi/DataPersistencyService.java b/cps/cps-service/src/main/java/org/onap/cps/spi/DataPersistencyService.java
new file mode 100644
index 0000000000..ce51f40ac6
--- /dev/null
+++ b/cps/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/cps-service/src/main/java/org/onap/cps/spi/ModelPersistencyService.java b/cps/cps-service/src/main/java/org/onap/cps/spi/ModelPersistencyService.java
new file mode 100644
index 0000000000..2afdaa82a8
--- /dev/null
+++ b/cps/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/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/main/resources/logback-spring.xml b/cps/cps-service/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000000..8a4ae07c5b
--- /dev/null
+++ b/cps/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{&quot;yyyy-MM-dd'T'HH:mm:ss.SSSXXX&quot;,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/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
new file mode 100644
index 0000000000..5f42810bd5
--- /dev/null
+++ b/cps/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/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..07647520b0
--- /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
diff --git a/cps/pom.xml b/cps/pom.xml
new file mode 100644
index 0000000000..c2d6c7994d
--- /dev/null
+++ b/cps/pom.xml
@@ -0,0 +1,37 @@
+<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.oparent</groupId>
+ <artifactId>oparent</artifactId>
+ <version>3.1.0</version>
+ </parent>
+
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-aggregator</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <name>cps</name>
+ <description>ONAP Configuration and Persistency Service</description>
+
+ <organization>
+ <name>ONAP - CPS</name>
+ <url>http://www.onap.org/</url>
+ </organization>
+
+ <properties>
+ <maven.deploy.skip>true</maven.deploy.skip>
+ <maven.install.skip>true</maven.install.skip>
+ </properties>
+
+ <modules>
+ <module>cps-dependencies</module>
+ <module>cps-bom</module>
+ <module>cps-parent</module>
+ <module>cps-service</module>
+ <module>cps-rest</module>
+ <module>cps-ri</module>
+ </modules>
+</project>
diff --git a/cps/version.properties b/cps/version.properties
new file mode 100644
index 0000000000..12156a9a64
--- /dev/null
+++ b/cps/version.properties
@@ -0,0 +1,12 @@
+# Note that these variables cannot be structured (e.g. : version.release or version.snapshot etc... )
+# because they are used in Jenkins, whose plug-in doesn't support this
+
+major=1
+minor=0
+patch=0
+
+base_version=${major}.${minor}.${patch}
+
+# Release must be completed with git revision # in Jenkins
+release_version=${base_version}
+snapshot_version=${base_version}-SNAPSHOT