From 09c6b6e1fa2684c913d7b904f7c7ad6b26b04ef1 Mon Sep 17 00:00:00 2001 From: niamhcore Date: Mon, 22 Nov 2021 11:44:38 +0000 Subject: [PATCH] CPS-314: Delete Dataspace Issue-ID: CPS-314 Change-Id: I778e2b784c7b1ff3fecc1036425708dc4ec73227 Signed-off-by: niamhcore --- cps-rest/docs/openapi/cpsAdmin.yml | 20 +++++++++++ .../cps/rest/controller/AdminRestController.java | 12 +++++++ .../rest/controller/AdminRestControllerSpec.groovy | 13 +++++++ .../exceptions/CpsRestExceptionHandlerSpec.groovy | 4 ++- .../spi/impl/CpsAdminPersistenceServiceImpl.java | 17 +++++++++ .../onap/cps/spi/repository/AnchorRepository.java | 2 ++ .../cps/spi/repository/SchemaSetRepository.java | 5 ++- .../spi/impl/CpsAdminPersistenceServiceSpec.groovy | 29 ++++++++++++++-- cps-ri/src/test/resources/data/anchor.sql | 8 +++-- .../java/org/onap/cps/api/CpsAdminService.java | 7 ++++ .../org/onap/cps/api/impl/CpsAdminServiceImpl.java | 5 +++ .../onap/cps/spi/CpsAdminPersistenceService.java | 7 ++++ .../spi/exceptions/DataspaceInUseException.java | 40 ++++++++++++++++++++++ .../cps/api/impl/CpsAdminServiceImplSpec.groovy | 8 +++++ .../cps/spi/exceptions/CpsExceptionsSpec.groovy | 8 +++++ 15 files changed, 176 insertions(+), 9 deletions(-) create mode 100644 cps-service/src/main/java/org/onap/cps/spi/exceptions/DataspaceInUseException.java diff --git a/cps-rest/docs/openapi/cpsAdmin.yml b/cps-rest/docs/openapi/cpsAdmin.yml index a022ef1d9..869cb6e72 100644 --- a/cps-rest/docs/openapi/cpsAdmin.yml +++ b/cps-rest/docs/openapi/cpsAdmin.yml @@ -35,6 +35,26 @@ dataspaces: '403': $ref: 'components.yml#/components/responses/Forbidden' + delete: + description: Delete a dataspace + tags: + - cps-admin + summary: Delete a dataspace + operationId: deleteDataspace + parameters: + - $ref: 'components.yml#/components/parameters/dataspaceNameInQuery' + responses: + '204': + $ref: 'components.yml#/components/responses/NoContent' + '400': + $ref: 'components.yml#/components/responses/BadRequest' + '401': + $ref: 'components.yml#/components/responses/Unauthorized' + '403': + $ref: 'components.yml#/components/responses/Forbidden' + '409': + $ref: 'components.yml#/components/responses/Conflict' + schemaSet: post: description: Create a new schema set in the given dataspace 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 55fdbbe87..52e64a95b 100755 --- 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 @@ -70,6 +70,18 @@ public class AdminRestController implements CpsAdminApi { return new ResponseEntity<>(dataspaceName, HttpStatus.CREATED); } + /** + * Delete a dataspace. + * + * @param dataspaceName name of dataspace to be deleted + * @return a {@Link ResponseEntity} of {@link HttpStatus} NO_CONTENT + */ + @Override + public ResponseEntity deleteDataspace(final String dataspaceName) { + cpsAdminService.deleteDataspace(dataspaceName); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + /** * Create a {@link SchemaSet}. * 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 84da2db5d..e8cfcfb6f 100755 --- 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 @@ -311,6 +311,18 @@ class AdminRestControllerSpec extends Specification { response.status == HttpStatus.NO_CONTENT.value() } + def 'Delete dataspace.'() { + given: 'an endpoint' + def dataspaceEndpoint = "$basePath/v1/dataspaces" + when: 'delete dataspace endpoint is invoked' + def response = mvc.perform(delete(dataspaceEndpoint) + .param('dataspace-name', dataspaceName)) + .andReturn().response + then: 'associated service method is invoked with expected parameter' + 1 * mockCpsAdminService.deleteDataspace(dataspaceName) + and: 'response code indicates success' + response.status == HttpStatus.NO_CONTENT.value() + } def createMultipartFile(filename, content) { return new MockMultipartFile("file", filename, "text/plain", content.getBytes()) @@ -333,4 +345,5 @@ class AdminRestControllerSpec extends Specification { multipartFile.getInputStream() >> { throw new IOException() } return multipartFile } + } diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy index 079a59c66..f5968442d 100644 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy @@ -37,6 +37,7 @@ import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.exceptions.ModelValidationException import org.onap.cps.spi.exceptions.NotFoundInDataspaceException import org.onap.cps.spi.exceptions.SchemaSetInUseException +import org.onap.cps.spi.exceptions.DataspaceInUseException import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value @@ -145,7 +146,8 @@ class CpsRestExceptionHandlerSpec extends Specification { assertTestResponse(response, CONFLICT, exceptionThrown.getMessage(), exceptionThrown.getDetails()) where: 'the following exceptions are thrown' exceptionThrown << [new DataInUseException(dataspaceName, existingObjectName), - new SchemaSetInUseException(dataspaceName, existingObjectName)] + new SchemaSetInUseException(dataspaceName, existingObjectName), + new DataspaceInUseException(dataspaceName, errorDetails)] } /* 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 b1bd03c25..9c69006ec 100755 --- 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 @@ -32,6 +32,7 @@ import org.onap.cps.spi.entities.AnchorEntity; import org.onap.cps.spi.entities.DataspaceEntity; import org.onap.cps.spi.entities.YangResourceModuleReference; import org.onap.cps.spi.exceptions.AlreadyDefinedException; +import org.onap.cps.spi.exceptions.DataspaceInUseException; import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException; import org.onap.cps.spi.model.Anchor; import org.onap.cps.spi.repository.AnchorRepository; @@ -70,6 +71,22 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic } } + @Override + public void deleteDataspace(final String dataspaceName) { + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); + final int numberOfAssociatedAnchors = anchorRepository.countByDataspace(dataspaceEntity); + if (numberOfAssociatedAnchors != 0) { + throw new DataspaceInUseException(dataspaceName, + String.format("Dataspace contains %d anchor(s)", numberOfAssociatedAnchors)); + } + final int numberOfAssociatedSchemaSets = schemaSetRepository.countByDataspace(dataspaceEntity); + if (numberOfAssociatedSchemaSets != 0) { + throw new DataspaceInUseException(dataspaceName, + String.format("Dataspace contains %d schemaset(s)", numberOfAssociatedSchemaSets)); + } + dataspaceRepository.delete(dataspaceEntity); + } + @Override public void createAnchor(final String dataspaceName, final String schemaSetName, final String anchorName) { final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName); diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java index 5870fd9e9..471f1758c 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java @@ -45,6 +45,8 @@ public interface AnchorRepository extends JpaRepository { Collection findAllBySchemaSet(@NotNull SchemaSetEntity schemaSetEntity); + Integer countByDataspace(@NotNull DataspaceEntity dataspaceEntity); + @Query(value = "SELECT anchor.* FROM yang_resource\n" + "JOIN schema_set_yang_resources ON schema_set_yang_resources.yang_resource_id = yang_resource.id\n" + "JOIN schema_set ON schema_set.id = schema_set_yang_resources.schema_set_id\n" diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java index 7b56f9323..a15ce622c 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java @@ -19,7 +19,6 @@ package org.onap.cps.spi.repository; -import java.util.List; import java.util.Optional; import javax.validation.constraints.NotNull; import org.onap.cps.spi.entities.DataspaceEntity; @@ -31,11 +30,11 @@ import org.springframework.stereotype.Repository; @Repository public interface SchemaSetRepository extends JpaRepository { - List findAllByDataspace(@NotNull DataspaceEntity dataspaceEntity); - Optional findByDataspaceAndName(@NotNull DataspaceEntity dataspaceEntity, @NotNull String schemaSetName); + Integer countByDataspace(@NotNull DataspaceEntity dataspaceEntity); + /** * Gets a schema set by dataspace and schema set name. * diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy index a0df2b169..4b5b116f4 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy @@ -24,6 +24,7 @@ package org.onap.cps.spi.impl import org.onap.cps.spi.CpsAdminPersistenceService import org.onap.cps.spi.exceptions.AlreadyDefinedException import org.onap.cps.spi.exceptions.AnchorNotFoundException +import org.onap.cps.spi.exceptions.DataspaceInUseException import org.onap.cps.spi.exceptions.DataspaceNotFoundException import org.onap.cps.spi.exceptions.SchemaSetNotFoundException import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException @@ -36,10 +37,9 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase { @Autowired CpsAdminPersistenceService objectUnderTest - static final String SET_DATA = '/data/anchor.sql' static final String SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES = '/data/anchors-schemaset-modules.sql' - static final String EMPTY_DATASPACE_NAME = 'DATASPACE-002' + static final String DATASPACE_WITH_NO_DATA = 'DATASPACE-002' static final Integer DELETED_ANCHOR_ID = 3001 static final Long DELETED_FRAGMENT_ID = 4001 @@ -111,7 +111,7 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase { dataspaceName || expectedAnchors DATASPACE_NAME || [Anchor.builder().name(ANCHOR_NAME1).schemaSetName(SCHEMA_SET_NAME1).dataspaceName(DATASPACE_NAME).build(), Anchor.builder().name(ANCHOR_NAME2).schemaSetName(SCHEMA_SET_NAME2).dataspaceName(DATASPACE_NAME).build()] - EMPTY_DATASPACE_NAME || [] + DATASPACE_WITH_NO_DATA || [] } @Sql(CLEAR_DATA) @@ -173,4 +173,27 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase { def buildAnchor(def anchorName, def dataspaceName, def SchemaSetName) { return Anchor.builder().name(anchorName).dataspaceName(dataspaceName).schemaSetName(SchemaSetName).build() } + + @Sql([CLEAR_DATA, SET_DATA]) + def 'Delete dataspace.'() { + when: 'delete dataspace action is invoked' + objectUnderTest.deleteDataspace(DATASPACE_WITH_NO_DATA) + then: 'dataspace is deleted' + assert dataspaceRepository.findByName(DATASPACE_WITH_NO_DATA).isEmpty(); + } + + @Sql([CLEAR_DATA, SET_DATA]) + def 'Delete dataspace when #scenario.'() { + when: 'delete dataspace action is invoked' + objectUnderTest.deleteDataspace(dataspaceName) + then: 'the correct exception is thrown with the relevant details' + def thrownException = thrown(expectedException) + thrownException.details.contains(expectedMessageDetails) + where: 'the following data is used' + scenario | dataspaceName || expectedException | expectedMessageDetails + 'dataspace name does not exist' | 'unknown' || DataspaceNotFoundException | 'unknown does not exist' + 'dataspace contains an anchor' | 'DATASPACE-001' || DataspaceInUseException | 'contains 2 anchor(s)' + 'dataspace contains schemasets' | 'DATASPACE-003' || DataspaceInUseException | 'contains 1 schemaset(s)' + } + } diff --git a/cps-ri/src/test/resources/data/anchor.sql b/cps-ri/src/test/resources/data/anchor.sql index dbf1a6a47..c9240f7fd 100644 --- a/cps-ri/src/test/resources/data/anchor.sql +++ b/cps-ri/src/test/resources/data/anchor.sql @@ -21,10 +21,14 @@ */ INSERT INTO DATASPACE (ID, NAME) VALUES - (1001, 'DATASPACE-001'), (1002, 'DATASPACE-002'); + (1001, 'DATASPACE-001'), + (1002, 'DATASPACE-002'), + (1003, 'DATASPACE-003'); INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES - (2001, 'SCHEMA-SET-001', 1001), (2002, 'SCHEMA-SET-002', 1001); + (2001, 'SCHEMA-SET-001', 1001), + (2002, 'SCHEMA-SET-002', 1001), + (2003, 'SCHEMA-SET-002', 1003); INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES (3001, 'ANCHOR-001', 1001, 2001), 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 1d08cde7b..7ba95995a 100755 --- a/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java @@ -41,6 +41,13 @@ public interface CpsAdminService { */ void createDataspace(@NonNull String dataspaceName); + /** + * Delete dataspace. + * + * @param dataspaceName the name of the dataspace to delete + */ + void deleteDataspace(@NonNull String dataspaceName); + /** * Create an Anchor. * 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 faff7b611..d83179326 100755 --- 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 @@ -41,6 +41,11 @@ public class CpsAdminServiceImpl implements CpsAdminService { cpsAdminPersistenceService.createDataspace(dataspaceName); } + @Override + public void deleteDataspace(final String dataspaceName) { + cpsAdminPersistenceService.deleteDataspace(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 104ac4f3f..95537006a 100755 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java @@ -40,6 +40,13 @@ public interface CpsAdminPersistenceService { */ void createDataspace(@NonNull String dataspaceName); + /** + * Delete dataspace. + * + * @param dataspaceName the name of the dataspace to delete + */ + void deleteDataspace(@NonNull String dataspaceName); + /** * Create an Anchor. * diff --git a/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataspaceInUseException.java b/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataspaceInUseException.java new file mode 100644 index 000000000..7889301dd --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataspaceInUseException.java @@ -0,0 +1,40 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.exceptions; + +/** + * Runtime exception. + * Thrown when given dataspace name is rejected to be deleted because it has anchor or schemasets associated. + */ + +public class DataspaceInUseException extends DataInUseException { + + private static final long serialVersionUID = 4531370947720760347L; + + /** + * Constructor. + * + * @param dataspaceName dataspace name + * @param details error message details + */ + public DataspaceInUseException(final String dataspaceName, final String details) { + super(String.format("Dataspace with name %s is being used.", dataspaceName), details); + } +} 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 95afeb417..6d1f58629 100755 --- 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 @@ -78,4 +78,12 @@ class CpsAdminServiceImplSpec extends Specification { objectUnderTest.queryAnchorNames('some-dataspace-name', ['some-module-name']) == ['some-anchor-identifier'] } + + def 'Delete dataspace.'() { + when: 'delete dataspace is invoked' + objectUnderTest.deleteDataspace('someDataspace') + then: 'associated persistence service method is invoked with correct parameter' + 1 * mockCpsAdminPersistenceService.deleteDataspace('someDataspace') + } + } diff --git a/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy index 4243c18c2..5bd367834 100755 --- a/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy @@ -170,4 +170,12 @@ class CpsExceptionsSpec extends Specification { expect: 'the exception has the provided details' exception.details == providedDetails } + + def 'Creating an exception that the dataspace is being used and cannot be deleted.'() { + given: 'a dataspace in use exception is created' + def exception = new DataspaceInUseException(dataspaceName,providedDetails) + expect: 'the exception has the correct message with dataspace name and provided details' + exception.message == "Dataspace with name ${dataspaceName} is being used." + exception.details == providedDetails + } } -- 2.16.6