diff options
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{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",UTC}"/> + + <property name="debugPattern" + value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%thread|%X{RequestID}| %logger{50} - %msg%n" /> + + <appender name="Debug" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>../log/${logName}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logName}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>${maxFileSize}</maxFileSize> + <maxHistory>${maxHistory}</maxHistory> + <totalSizeCap>${totalSizeCap}</totalSizeCap> + </rollingPolicy> + <encoder> + <pattern>${debugPattern}</pattern> + </encoder> + </appender> + + <appender name="asyncDebug" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <appender-ref ref="Debug" /> + <includeCallerData>true</includeCallerData> + </appender> + + <logger name="org.onap.cps" level="DEBUG" additivity="false"> + <appender-ref ref="asyncDebug" /> + </logger> + + + <root level="INFO"> + <appender-ref ref="asyncDebug" /> + </root> + +</configuration>
\ No newline at end of file diff --git a/cps/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{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",UTC}"/> + + <property name="debugPattern" + value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%thread|%X{RequestID}| %logger{50} - %msg%n" /> + + <appender name="Debug" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>../log/${logName}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logName}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>${maxFileSize}</maxFileSize> + <maxHistory>${maxHistory}</maxHistory> + <totalSizeCap>${totalSizeCap}</totalSizeCap> + </rollingPolicy> + <encoder> + <pattern>${debugPattern}</pattern> + </encoder> + </appender> + + <appender name="asyncDebug" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <appender-ref ref="Debug" /> + <includeCallerData>true</includeCallerData> + </appender> + + <logger name="org.onap.cps" level="DEBUG" additivity="false"> + <appender-ref ref="asyncDebug" /> + </logger> + + + <root level="INFO"> + <appender-ref ref="asyncDebug" /> + </root> + +</configuration>
\ No newline at end of file diff --git a/cps/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{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",UTC}"/> + + <property name="debugPattern" + value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%thread|%X{RequestID}| %logger{50} - %msg%n" /> + + <appender name="Debug" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>../log/${logName}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logName}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>${maxFileSize}</maxFileSize> + <maxHistory>${maxHistory}</maxHistory> + <totalSizeCap>${totalSizeCap}</totalSizeCap> + </rollingPolicy> + <encoder> + <pattern>${debugPattern}</pattern> + </encoder> + </appender> + + <appender name="asyncDebug" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <appender-ref ref="Debug" /> + <includeCallerData>true</includeCallerData> + </appender> + + <logger name="org.onap.cps" level="DEBUG" additivity="false"> + <appender-ref ref="asyncDebug" /> + </logger> + + + <root level="INFO"> + <appender-ref ref="asyncDebug" /> + </root> + +</configuration>
\ No newline at end of file diff --git a/cps/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 |