VSE: Upload modules (a model file) to a (new) dataspace
authorputhuparambil.aditya <aditya.puthuparambil@bell.ca>
Tue, 20 Oct 2020 11:23:34 +0000 (12:23 +0100)
committerputhuparambil.aditya <aditya.puthuparambil@bell.ca>
Tue, 27 Oct 2020 11:32:30 +0000 (11:32 +0000)
1. Schema.sql modified to include modules and json_data tables which can be removed after the PoC
2. URI changed as per the proposal to '/dataspaces/{dataspace_name}/modules'
3. Dataspace name corresponding to a model is passed as a parameter.
4. In case the dataspace doesnt exist in the dataspace table, a new entry for the passed dataspace is created.
5. The corresponding dataspace_id is also stored as  a reference in the modules table.
6. Test case for Rest API will be pushed as another review.

JIRA: https://jira.onap.org/browse/CCSDK-2897

Issue-ID: CCSDK-2897
Change-Id: Ic9caa39b5a7afca28c0365cdb4f492848d0ead3e
Signed-off-by: puthuparambil.aditya <aditya.puthuparambil@bell.ca>
14 files changed:
cps/README.md
cps/cps-rest/src/main/java/org/onap/cps/rest/controller/RestController.java
cps/cps-ri/src/main/java/org/onap/cps/spi/entities/Dataspace.java
cps/cps-ri/src/main/java/org/onap/cps/spi/entities/ModuleEntity.java
cps/cps-ri/src/main/java/org/onap/cps/spi/impl/ModelPersistencyServiceImpl.java
cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java [new file with mode: 0644]
cps/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleRepository.java
cps/cps-ri/src/main/resources/schema.sql
cps/cps-service/pom.xml
cps/cps-service/src/main/java/org/onap/cps/api/CpService.java
cps/cps-service/src/main/java/org/onap/cps/api/impl/CpServiceImpl.java
cps/cps-service/src/main/java/org/onap/cps/spi/ModelPersistencyService.java
cps/cps-service/src/test/groovy/org/onap/cps/api/impl/CpServiceImplSpec.groovy
cps/pom.xml

index 791015e..d1bf49d 100644 (file)
@@ -1,7 +1,7 @@
-# Configuration & Persistency Service
+# Configuration Persistence Service
 
 This folder contains all files for
-[Configuration & Persistency Service](https://wiki.onap.org/pages/viewpage.action?pageId=81406119).
+[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.
index 18e24b4..703e778 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============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.
@@ -22,8 +23,6 @@ package org.onap.cps.rest.controller;
 import com.google.gson.Gson;
 import com.google.gson.JsonSyntaxException;
 import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
 import javax.persistence.PersistenceException;
 import javax.validation.Valid;
 import javax.ws.rs.Consumes;
@@ -36,10 +35,9 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.SecurityContext;
 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
-import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
 import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.hibernate.exception.ConstraintViolationException;
 import org.onap.cps.api.CpService;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
@@ -110,19 +108,21 @@ public class RestController implements CpsResourceApi {
      * Upload a yang model file.
      *
      * @param uploadedFile the yang model file.
+     * @param dataspaceName the dataspace name linked to the model.
      * @return a http response code.
      */
     @POST
-    @Path("/upload-yang-model-file")
+    @Path("/dataspaces/{dataspace_name}/modules")
     @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.MULTIPART_FORM_DATA)
-    public final Response uploadYangModelFile(@FormDataParam("file") File uploadedFile) throws IOException {
+    public final Response uploadYangModelFile(@FormDataParam("file") File uploadedFile,
+        @PathParam("dataspace_name") String dataspaceName) {
         try {
             final File fileToParse = renameFileIfNeeded(uploadedFile);
             final SchemaContext schemaContext = cpService.parseAndValidateModel(fileToParse);
-            cpService.storeSchemaContext(schemaContext);
-            return Response.status(Status.OK).entity("Yang File Parsed").build();
-        } catch (final YangParserException e) {
+            cpService.storeSchemaContext(schemaContext, dataspaceName);
+            return Response.status(Status.CREATED).entity("Resource successfully created").build();
+        } catch (final YangParserException | ConstraintViolationException e) {
             return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
         } catch (final Exception e) {
             return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
index 8f37692..627a144 100644 (file)
@@ -31,6 +31,7 @@ import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
 
+
 /**
  * Entity to store a dataspace.
  */
@@ -49,4 +50,13 @@ public class Dataspace {
     @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
index f786c58..d2130ae 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============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.
@@ -21,10 +22,14 @@ 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;
@@ -39,32 +44,41 @@ import lombok.Setter;
 @Entity
 @AllArgsConstructor
 @NoArgsConstructor
-@Table(name = "modules")
+@Table(name = "module")
 public class ModuleEntity {
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Integer id;
 
-    @Column
-    private String name;
-
+    @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 name the module name.
+     * @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 name, String moduleContent, String revision) {
-        this.name = name;
+    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
index 14085c7..01c7a7b 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============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.
@@ -20,7 +21,9 @@
 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;
@@ -31,15 +34,24 @@ public class ModelPersistencyServiceImpl implements ModelPersistencyService {
 
     private final ModuleRepository moduleRepository;
 
+    private final DataspaceRepository dataspaceRepository;
+
     @Autowired
-    public ModelPersistencyServiceImpl(final ModuleRepository moduleRepository) {
+    public ModelPersistencyServiceImpl(final ModuleRepository moduleRepository,
+        final DataspaceRepository dataspaceRepository) {
         this.moduleRepository = moduleRepository;
+        this.dataspaceRepository = dataspaceRepository;
     }
 
     @Override
-    public void storeModule(final String name, final String moduleContent, final String revision) {
-        final ModuleEntity moduleEntity = new ModuleEntity(name, moduleContent, revision);
+    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/DataspaceRepository.java b/cps/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java
new file mode 100644 (file)
index 0000000..46a5266
--- /dev/null
@@ -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
index 0fe53b6..f9078d7 100644 (file)
@@ -26,5 +26,4 @@ import org.springframework.stereotype.Repository;
 
 @Repository
 public interface ModuleRepository extends JpaRepository<ModuleEntity, Integer> {
-
 }
\ No newline at end of file
index 05d31d9..6a76fbd 100644 (file)
@@ -17,12 +17,6 @@ CREATE TABLE IF NOT EXISTS SCHEMA_NODE
     ID SERIAL PRIMARY KEY\r
 );\r
 \r
-CREATE TABLE IF NOT EXISTS MODULE_SET\r
-(\r
-    MODULE_SET_REFERENCE TEXT NOT NULL,\r
-    ID SERIAL PRIMARY KEY\r
-);\r
-\r
 CREATE TABLE IF NOT EXISTS FRAGMENT\r
 (\r
     ID BIGSERIAL PRIMARY KEY,\r
@@ -45,6 +39,17 @@ CREATE TABLE IF NOT EXISTS RELATION
     CONSTRAINT RELATION_PKEY PRIMARY KEY (TO_FRAGMENT_ID, FROM_FRAGMENT_ID, RELATION_TYPE_ID)\r
 );\r
 \r
+CREATE TABLE IF NOT EXISTS MODULE\r
+(\r
+    NAMESPACE TEXT NOT NULL,\r
+    REVISION TEXT NOT NULL,\r
+    MODULE_CONTENT TEXT NOT NULL,\r
+    DATASPACE_ID BIGINT NOT NULL,\r
+    ID SERIAL PRIMARY KEY,\r
+    UNIQUE (NAMESPACE, REVISION),\r
+    CONSTRAINT module_dataspace FOREIGN KEY (DATASPACE_ID) REFERENCES DATASPACE (id) ON UPDATE CASCADE ON DELETE CASCADE\r
+);\r
+\r
 CREATE INDEX  IF NOT EXISTS "FKI_FRAGMENT_DATASPACE_ID_FK"     ON FRAGMENT USING BTREE(DATASPACE_ID) ;\r
 CREATE INDEX  IF NOT EXISTS "FKI_FRAGMENT_MODULE_SET_ID_FK"    ON FRAGMENT USING BTREE(MODULE_SET_ID) ;\r
 CREATE INDEX  IF NOT EXISTS "FKI_FRAGMENT_PARENT_ID_FK"        ON FRAGMENT USING BTREE(PARENT_ID) ;\r
index 7318734..ea1bdad 100644 (file)
     </dependency>\r
 \r
     <!-- T E S T   D E P E N D E N C I E S -->\r
-\r
     <dependency>\r
       <groupId>org.codehaus.groovy</groupId>\r
       <artifactId>groovy</artifactId>\r
-      <version>${groovy.version}</version>\r
-      <scope>test</scope>\r
     </dependency>\r
     <dependency>\r
       <groupId>org.spockframework</groupId>\r
       <artifactId>spock-core</artifactId>\r
-      <version>${spock-core.version}</version>\r
-      <scope>test</scope>\r
     </dependency>\r
     <dependency>\r
       <groupId>cglib</groupId>\r
       <artifactId>cglib-nodep</artifactId>\r
-      <version>3.1</version>\r
-      <scope>test</scope>\r
     </dependency>\r
+\r
   </dependencies>\r
 \r
 </project>\r
index 177fc04..2f735ab 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============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.
@@ -49,8 +50,9 @@ public interface CpService {
      * Store schema context for a yang model.
      *
      * @param schemaContext the schema context
+     * @param dataspaceName the dataspace name
      */
-    void storeSchemaContext(final SchemaContext schemaContext);
+    void storeSchemaContext(final SchemaContext schemaContext, final String dataspaceName);
 
     /**
      * Store the JSON structure in the database.
index 80a685b..45aad3e 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============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.
 
 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.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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -40,15 +40,12 @@ import org.springframework.stereotype.Component;
 @Component
 public class CpServiceImpl implements CpService {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(CpServiceImpl.class);
-
     @Autowired
     private ModelPersistencyService modelPersistencyService;
 
     @Autowired
     private DataPersistencyService dataPersistencyService;
 
-
     @Override
     public final SchemaContext parseAndValidateModel(final String yangModelContent) throws IOException,
         YangParserException {
@@ -76,13 +73,16 @@ public class CpServiceImpl implements CpService {
 
     @Override
     public void deleteJsonById(int jsonObjectId) {
-        dataPersistencyService.deleteJsonById(jsonObjectId);;
+        dataPersistencyService.deleteJsonById(jsonObjectId);
     }
 
     @Override
-    public final void storeSchemaContext(final SchemaContext schemaContext) {
+    public final void storeSchemaContext(final SchemaContext schemaContext, final String dataspaceName) {
         for (final Module module : schemaContext.getModules()) {
-            modelPersistencyService.storeModule(module.getName(), module.toString(), module.getRevision().toString());
+            Optional<Revision> optionalRevision = module.getRevision();
+            String revisionValue = optionalRevision.isPresent() ? optionalRevision.get().toString() : null;
+            modelPersistencyService.storeModule(module.getNamespace().toString(), module.toString(),
+                revisionValue, dataspaceName);
         }
     }
 }
index f88c6b2..2afdaa8 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============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.
@@ -27,10 +28,12 @@ public interface ModelPersistencyService {
     /**
      * Store the module from a yang model in the database.
      *
-     * @param name module name
+     * @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 name, final String moduleContent, final String revision);
+    void storeModule(final String namespace, final String moduleContent, final String revision,
+        final String dataspaceName);
 
 }
index a2f9b35..5e6fccb 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * ============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.
@@ -21,6 +22,7 @@ package org.onap.cps.api.impl
 
 import org.onap.cps.TestUtils
 import org.onap.cps.spi.DataPersistencyService
+
 import org.opendaylight.yangtools.yang.common.Revision
 import org.opendaylight.yangtools.yang.model.api.SchemaContext
 import org.opendaylight.yangtools.yang.model.parser.api.YangParserException
@@ -32,7 +34,7 @@ class CpServiceImplSpec extends Specification {
     def objectUnderTest = new CpServiceImpl()
 
     def setup() {
-        objectUnderTest.dataPersistencyService = mockDataPersistencyService;
+        objectUnderTest.dataPersistencyService = mockDataPersistencyService
     }
 
     def 'Cps Service provides to its client the id assigned by the system when storing a data structure'() {
@@ -77,7 +79,7 @@ class CpServiceImplSpec extends Specification {
 
     def 'Store a SchemaContext'() {
         expect: 'No exception to be thrown when a valid model (schema) is stored'
-            objectUnderTest.storeSchemaContext(Stub(SchemaContext.class))
+            objectUnderTest.storeSchemaContext(Stub(SchemaContext.class), "sampleDataspace")
     }
 
     def 'Read a JSON object with a valid identifier'(){
index dd98ca5..892492d 100644 (file)
                 <type>pom</type>\r
                 <scope>import</scope>\r
             </dependency>\r
+\r
+            <!-- T E S T   D E P E N D E N C I E S -->\r
+            <dependency>\r
+                <groupId>org.codehaus.groovy</groupId>\r
+                <artifactId>groovy</artifactId>\r
+                <version>${groovy.version}</version>\r
+                <scope>test</scope>\r
+            </dependency>\r
+            <dependency>\r
+                <groupId>org.spockframework</groupId>\r
+                <artifactId>spock-core</artifactId>\r
+                <version>${spock-core.version}</version>\r
+                <scope>test</scope>\r
+            </dependency>\r
+            <dependency>\r
+                <groupId>cglib</groupId>\r
+                <artifactId>cglib-nodep</artifactId>\r
+                <version>3.1</version>\r
+                <scope>test</scope>\r
+            </dependency>\r
+\r
         </dependencies>\r
     </dependencyManagement>\r
 \r