Create dataspace

Issue-ID: CPS-134
Change-Id: Ie7f00f9c322a12a6c2a71c1407f6970a7dd24d2d
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
diff --git a/cps-rest/docs/api/swagger/openapi.yml b/cps-rest/docs/api/swagger/openapi.yml
index d76ec5e..d2c720e 100755
--- a/cps-rest/docs/api/swagger/openapi.yml
+++ b/cps-rest/docs/api/swagger/openapi.yml
@@ -9,6 +9,35 @@
   - name: cps-rest
     description: cps Resource
 paths:
+  /v1/dataspaces:
+    post:
+      tags:
+        - cps-admin
+      summary: Create a new dataspace
+      operationId: createDataspace
+      parameters:
+        - name: dataspace-name
+          in: query
+          description: dataspace-name
+          required: true
+          schema:
+            type: string
+      responses:
+        201:
+          description: Created
+          content:
+            application/json:
+              schema:
+                type: string
+        400:
+          description: Bad Request
+          content: { }
+        401:
+          description: Unauthorized
+          content: { }
+        403:
+          description: Forbidden
+          content: { }
   /v1/dataspaces/{dataspace-name}/:
     delete:
       tags:
diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/AdminRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/AdminRestController.java
index 6dc2cee..9549580 100644
--- a/cps-rest/src/main/java/org/onap/cps/rest/controller/AdminRestController.java
+++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/AdminRestController.java
@@ -47,6 +47,12 @@
     private ModelMapper modelMapper;
 
     @Override
+    public ResponseEntity<String> createDataspace(final String dataspaceName) {
+        cpsAdminService.createDataspace(dataspaceName);
+        return new ResponseEntity<>(dataspaceName, HttpStatus.CREATED);
+    }
+
+    @Override
     public ResponseEntity<String> createSchemaSet(final String schemaSetName, final MultipartFile multipartFile,
         final String dataspaceName) {
         cpsModuleService.createSchemaSet(dataspaceName, schemaSetName, extractYangResourcesMap(multipartFile));
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
index ed87e3c..9919649 100644
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
@@ -24,6 +24,7 @@
 import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.spi.model.Anchor
+import org.onap.cps.spi.exceptions.DataspaceAlreadyDefinedException
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
@@ -60,6 +61,25 @@
     def anchor = new Anchor(name: 'my_anchor')
     def anchorList = [anchor]
 
+    def 'Create new dataspace'() {
+        when:
+            def response = performCreateDataspaceRequest("new-dataspace")
+        then: 'Service method is invoked with expected parameters'
+            1 * mockCpsAdminService.createDataspace("new-dataspace")
+        and:
+            response.status == HttpStatus.CREATED.value()
+    }
+
+    def 'Create dataspace over existing with same name'() {
+        given:
+            def thrownException = new DataspaceAlreadyDefinedException("", new RuntimeException())
+            mockCpsAdminService.createDataspace("existing-dataspace") >> { throw thrownException }
+        when:
+            def response = performCreateDataspaceRequest("existing-dataspace")
+        then:
+            response.status == HttpStatus.BAD_REQUEST.value()
+    }
+
     def 'Create schema set from yang file'() {
         def yangResourceMapCapture
         given:
@@ -83,6 +103,14 @@
             response.status == HttpStatus.BAD_REQUEST.value()
     }
 
+    def performCreateDataspaceRequest(String dataspaceName) {
+        return mvc.perform(
+                MockMvcRequestBuilders
+                        .post('/v1/dataspaces')
+                        .param('dataspace-name', dataspaceName)
+        ).andReturn().response
+    }
+
     def createMultipartFile(filename, content) {
         return new MockMultipartFile("file", filename, "text/plain", content.getBytes())
     }
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
index 5093ba5..d6579bd 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
@@ -28,6 +28,7 @@
 import org.onap.cps.spi.entities.Fragment;
 import org.onap.cps.spi.entities.SchemaSet;
 import org.onap.cps.spi.exceptions.AnchorAlreadyDefinedException;
+import org.onap.cps.spi.exceptions.DataspaceAlreadyDefinedException;
 import org.onap.cps.spi.model.Anchor;
 import org.onap.cps.spi.repository.DataspaceRepository;
 import org.onap.cps.spi.repository.FragmentRepository;
@@ -49,6 +50,15 @@
     private SchemaSetRepository schemaSetRepository;
 
     @Override
+    public void createDataspace(final String dataspaceName) {
+        try {
+            dataspaceRepository.save(new Dataspace(dataspaceName));
+        } catch (final DataIntegrityViolationException e) {
+            throw new DataspaceAlreadyDefinedException(dataspaceName, e);
+        }
+    }
+
+    @Override
     public void createAnchor(final String dataspaceName, final String schemaSetName, final String anchorName) {
         final Dataspace dataspace = dataspaceRepository.getByName(dataspaceName);
         final SchemaSet schemaSet = schemaSetRepository.getByDataspaceAndName(dataspace, schemaSetName);
diff --git a/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceTest.java b/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceTest.java
index 455369d..7497526 100644
--- a/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceTest.java
+++ b/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceTest.java
@@ -32,6 +32,7 @@
 import org.onap.cps.spi.entities.Dataspace;
 import org.onap.cps.spi.entities.Fragment;
 import org.onap.cps.spi.exceptions.AnchorAlreadyDefinedException;
+import org.onap.cps.spi.exceptions.DataspaceAlreadyDefinedException;
 import org.onap.cps.spi.exceptions.DataspaceNotFoundException;
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
 import org.onap.cps.spi.model.Anchor;
@@ -78,6 +79,24 @@
 
     @Test
     @SqlGroup({@Sql(CLEAR_DATA), @Sql(SET_DATA)})
+    public void testCreateDataspace() {
+        final String dataspaceName = "DATASPACE-NEW";
+        cpsAdminPersistenceService.createDataspace(dataspaceName);
+
+        final Dataspace dataspace = dataspaceRepository.findByName(dataspaceName).orElseThrow();
+        assertNotNull(dataspace);
+        assertNotNull(dataspace.getId());
+        assertEquals(dataspaceName, dataspace.getName());
+    }
+
+    @Test(expected = DataspaceAlreadyDefinedException.class)
+    @SqlGroup({@Sql(CLEAR_DATA), @Sql(SET_DATA)})
+    public void testCreateDataspaceWithNameAlreadyDefined() {
+        cpsAdminPersistenceService.createDataspace(DATASPACE_NAME);
+    }
+
+    @Test
+    @SqlGroup({@Sql(CLEAR_DATA), @Sql(SET_DATA)})
     public void testCreateAnchor() {
         cpsAdminPersistenceService.createAnchor(DATASPACE_NAME, SCHEMA_SET_NAME2, ANCHOR_NAME_NEW);
 
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java b/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java
index 5090e3a..581550f 100644
--- a/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java
+++ b/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java
@@ -23,6 +23,7 @@
 import java.util.Collection;
 import org.checkerframework.checker.nullness.qual.NonNull;
 import org.onap.cps.spi.exceptions.CpsException;
+import org.onap.cps.spi.exceptions.DataspaceAlreadyDefinedException;
 import org.onap.cps.spi.model.Anchor;
 
 /**
@@ -31,6 +32,14 @@
 public interface CpsAdminService {
 
     /**
+     * Create dataspace.
+     *
+     * @param dataspaceName dataspace name
+     * @throws DataspaceAlreadyDefinedException if dataspace with same name already exists
+     */
+    void createDataspace(@NonNull String dataspaceName);
+
+    /**
      * Create an Anchor.
      *
      * @param dataspaceName dataspace name
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java
index f93a827..0cb85fd 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java
@@ -34,6 +34,11 @@
     private CpsAdminPersistenceService cpsAdminPersistenceService;
 
     @Override
+    public void createDataspace(final String dataspaceName) {
+        cpsAdminPersistenceService.createDataspace(dataspaceName);
+    }
+
+    @Override
     public void createAnchor(final String dataspaceName, final String schemaSetName, final String anchorName) {
         cpsAdminPersistenceService.createAnchor(dataspaceName, schemaSetName, anchorName);
     }
diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
index 1b7ddb7..ba53003 100644
--- a/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
@@ -23,6 +23,7 @@
 
 import java.util.Collection;
 import org.checkerframework.checker.nullness.qual.NonNull;
+import org.onap.cps.spi.exceptions.DataspaceAlreadyDefinedException;
 import org.onap.cps.spi.model.Anchor;
 
 /*
@@ -31,6 +32,14 @@
 public interface CpsAdminPersistenceService {
 
     /**
+     * Create dataspace.
+     *
+     * @param dataspaceName dataspace name
+     * @throws DataspaceAlreadyDefinedException if dataspace with same name already exists
+     */
+    void createDataspace(@NonNull String dataspaceName);
+
+    /**
      * Create an Anchor.
      *
      * @param dataspaceName dataspace name
diff --git a/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataspaceAlreadyDefinedException.java b/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataspaceAlreadyDefinedException.java
new file mode 100644
index 0000000..d6d933c
--- /dev/null
+++ b/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataspaceAlreadyDefinedException.java
@@ -0,0 +1,40 @@
+/*
+ *  ============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.spi.exceptions;
+
+/**
+ * Dataspace already defined exception. Indicates the dataspace with same name already exists.
+ */
+public class DataspaceAlreadyDefinedException extends CpsAdminException {
+
+    private static final long serialVersionUID = -5813793951842079228L;
+
+    /**
+     * Constructor.
+     *
+     * @param dataspaceName dataspace name
+     * @param cause         the cause of this exception
+     */
+    public DataspaceAlreadyDefinedException(final String dataspaceName, final Throwable cause) {
+        super("Duplicate Dataspace.",
+            String.format("Dataspace with name %s already exists.", dataspaceName),
+            cause);
+    }
+}
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
index 9e13c77..0222824 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
@@ -32,6 +32,13 @@
         objectUnderTest.cpsAdminPersistenceService = mockCpsAdminPersistenceService
     }
 
+    def 'Create dataspace method invokes persistence service'() {
+        when: 'Create dataspace method is invoked'
+            objectUnderTest.createDataspace('someDataspace')
+        then: 'The persistence service method is invoked with same parameters'
+            1 * mockCpsAdminPersistenceService.createDataspace('someDataspace')
+    }
+
     def 'Create anchor method invokes persistence service'() {
         when: 'Create anchor method is invoked'
             objectUnderTest.createAnchor('dummyDataspace', 'dummySchemaSet', 'dummyAnchorName')