Refactored Delete SchemaSet functionality 75/126675/9
authorRenu Kumari <renu.kumari@bell.ca>
Thu, 20 Jan 2022 17:07:38 +0000 (12:07 -0500)
committerRenu Kumari <renu.kumari@bell.ca>
Fri, 28 Jan 2022 20:07:41 +0000 (20:07 +0000)
- Added get anchors by schemaset in cpsAdminService
- Changed DeleteSchemaSet functionality
    - Use CPSAdminService to getAnchors associated with schemaset
    - Use CPSAdminService to delete Anchors
    - Moved Cascade allowed validation into Service from Persistence

Issue-ID: CPS-791
Signed-off-by: Renu Kumari <renu.kumari@bell.ca>
Change-Id: Ife7644551183cb8c3eb686a654b0a43a427ac1e5

13 files changed:
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy
cps-ri/src/test/resources/data/anchor.sql
cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy

index 9c69006..51b2482 100755 (executable)
@@ -1,7 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  * Copyright (C) 2020 Nordix Foundation.
- * Modifications Copyright (C) 2020 Bell Canada.
+ * Modifications Copyright (C) 2020-2022 Bell Canada.
  * Modifications Copyright (C) 2021 Pantheon.tech
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,6 +30,7 @@ import javax.transaction.Transactional;
 import org.onap.cps.spi.CpsAdminPersistenceService;
 import org.onap.cps.spi.entities.AnchorEntity;
 import org.onap.cps.spi.entities.DataspaceEntity;
+import org.onap.cps.spi.entities.SchemaSetEntity;
 import org.onap.cps.spi.entities.YangResourceModuleReference;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.DataspaceInUseException;
@@ -77,12 +78,12 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
         final int numberOfAssociatedAnchors = anchorRepository.countByDataspace(dataspaceEntity);
         if (numberOfAssociatedAnchors != 0) {
             throw new DataspaceInUseException(dataspaceName,
-                    String.format("Dataspace contains %d anchor(s)", numberOfAssociatedAnchors));
+                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));
+                String.format("Dataspace contains %d schemaset(s)", numberOfAssociatedSchemaSets));
         }
         dataspaceRepository.delete(dataspaceEntity);
     }
@@ -108,7 +109,17 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
     public Collection<Anchor> getAnchors(final String dataspaceName) {
         final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final Collection<AnchorEntity> anchorEntities = anchorRepository.findAllByDataspace(dataspaceEntity);
-        return anchorEntities.stream().map(CpsAdminPersistenceServiceImpl::toAnchor).collect(Collectors.toList());
+        return anchorEntities.stream().map(CpsAdminPersistenceServiceImpl::toAnchor).collect(Collectors.toSet());
+    }
+
+    @Override
+    public Collection<Anchor> getAnchors(final String dataspaceName, final String schemaSetName) {
+        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+        final SchemaSetEntity schemaSetEntity = schemaSetRepository.getByDataspaceAndName(
+            dataspaceEntity, schemaSetName);
+        return anchorRepository.findAllBySchemaSet(schemaSetEntity)
+            .stream().map(CpsAdminPersistenceServiceImpl::toAnchor)
+            .collect(Collectors.toSet());
     }
 
     @Override
index e0f5426..3e39a05 100755 (executable)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Nordix Foundation
- *  Modifications Copyright (C) 2020-2021 Bell Canada.
+ *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  Modifications Copyright (C) 2021 Pantheon.tech
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,17 +44,14 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.hibernate.exception.ConstraintViolationException;
-import org.onap.cps.spi.CascadeDeleteAllowed;
 import org.onap.cps.spi.CpsAdminPersistenceService;
 import org.onap.cps.spi.CpsModulePersistenceService;
-import org.onap.cps.spi.entities.AnchorEntity;
 import org.onap.cps.spi.entities.SchemaSetEntity;
 import org.onap.cps.spi.entities.YangResourceEntity;
 import org.onap.cps.spi.entities.YangResourceModuleReference;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.DuplicatedYangResourceException;
 import org.onap.cps.spi.exceptions.ModelValidationException;
-import org.onap.cps.spi.exceptions.SchemaSetInUseException;
 import org.onap.cps.spi.model.ModuleReference;
 import org.onap.cps.spi.repository.AnchorRepository;
 import org.onap.cps.spi.repository.DataspaceRepository;
@@ -172,21 +169,16 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
 
     @Override
     @Transactional
-    public void deleteSchemaSet(final String dataspaceName, final String schemaSetName,
-        final CascadeDeleteAllowed cascadeDeleteAllowed) {
+    public void deleteSchemaSet(final String dataspaceName, final String schemaSetName) {
         final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final var schemaSetEntity =
             schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName);
-
-        final Collection<AnchorEntity> anchorEntities = anchorRepository.findAllBySchemaSet(schemaSetEntity);
-        if (!anchorEntities.isEmpty()) {
-            if (cascadeDeleteAllowed != CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED) {
-                throw new SchemaSetInUseException(dataspaceName, schemaSetName);
-            }
-            fragmentRepository.deleteByAnchorIn(anchorEntities);
-            anchorRepository.deleteAll(anchorEntities);
-        }
         schemaSetRepository.delete(schemaSetEntity);
+    }
+
+    @Override
+    @Transactional
+    public void deleteUnusedYangResourceModules() {
         yangResourceRepository.deleteOrphans();
     }
 
@@ -277,6 +269,7 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
     /**
      * Convert the specified data integrity violation exception into a CPS duplicated Yang resource exception
      * if the cause of the error is a yang checksum database constraint violation.
+     *
      * @param originalException the original db exception.
      * @param yangResourceEntities the collection of Yang resources involved in the db failure.
      * @return an optional converted CPS duplicated Yang resource exception. The optional is empty if the original
@@ -307,6 +300,7 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
 
     /**
      * Get the name of the yang resource having the specified checksum.
+     *
      * @param checksum the checksum. Null is supported.
      * @param yangResourceEntities the list of yang resources to search among.
      * @return the name found or null if none.
@@ -323,6 +317,7 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
 
     /**
      * Get the checksum that caused the constraint violation exception.
+     *
      * @param exception the exception having the checksum in error.
      * @return the checksum in error or null if not found.
      */
index 4b5b116..2218014 100644 (file)
@@ -2,6 +2,7 @@
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Nordix Foundation
  *  Modifications Copyright (C) 2021 Pantheon.tech
+ *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -39,7 +40,7 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
 
     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 DATASPACE_WITH_NO_DATA = 'DATASPACE-002'
+    static final String DATASPACE_WITH_NO_DATA = 'DATASPACE-002-NO-DATA'
     static final Integer DELETED_ANCHOR_ID = 3001
     static final Long DELETED_FRAGMENT_ID = 4001
 
@@ -108,12 +109,36 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
             result.size() == expectedAnchors.size()
             result.containsAll(expectedAnchors)
         where: 'the following data is used'
-            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()]
+            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()]
             DATASPACE_WITH_NO_DATA || []
     }
 
+    @Sql([CLEAR_DATA, SET_DATA])
+    def 'Get all anchors associated with schemaset in a dataspace.'() {
+        when: 'anchors are retrieved by dataspace and schema-set'
+            def anchors = objectUnderTest.getAnchors(dataspace, schemasetName)
+        then: ' the response contains expected anchors'
+            anchors == expectedAnchors
+        where:
+            scenario     | dataspace       | schemasetName               || expectedAnchors
+            'no-anchors' | 'DATASPACE-003' | 'SCHEMA-SET-002-NO-ANCHORS' || Collections.emptySet()
+            'one-anchor' | 'DATASPACE-001' | 'SCHEMA-SET-001'            || Set.of(new Anchor('ANCHOR-001', 'DATASPACE-001', 'SCHEMA-SET-001'))
+    }
+
+    @Sql([CLEAR_DATA, SET_DATA])
+    def 'Error Handling: Get all anchors associated with schemaset in a dataspace.'() {
+        when: 'anchors are retrieved by dataspace and schema-set'
+            def anchors = objectUnderTest.getAnchors(dataspace, schemasetName)
+        then: ' an expected expception is thrown'
+            thrown(expectedException)
+        where:
+            scenario            | dataspace       | schemasetName               || expectedException
+            'unknown-dataspace' | 'unknown'       | 'SCHEMA-SET-002-NO-ANCHORS' || DataspaceNotFoundException
+            'unknown-schemaset' | 'DATASPACE-001' | 'unknown-schema-set'        || SchemaSetNotFoundException
+    }
+
     @Sql(CLEAR_DATA)
     def 'Get all anchors in unknown dataspace.'() {
         when: 'attempt to get all anchors in an unknown dataspace'
@@ -132,7 +157,7 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
-    def 'delete anchor error scenario: #scenario'(){
+    def 'delete anchor error scenario: #scenario'() {
         when: 'delete anchor attempt is performed'
             objectUnderTest.deleteAnchor(dataspaceName, anchorName)
         then: 'an #expectedException is thrown'
@@ -190,10 +215,10 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
             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)'
+            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)'
     }
 
 }
index a223e71..75d6330 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Nordix Foundation
- *  Modifications Copyright (C) 2021 Bell Canada.
+ *  Modifications Copyright (C) 2021-2022 Bell Canada.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the 'License');
  *  you may not use this file except in compliance with the License.
@@ -176,50 +176,37 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
-    def 'Delete schema set with cascade delete prohibited but no anchors using it'() {
+    def 'Delete schema set'() {
         when: 'a schema set is deleted with cascade-prohibited option'
-            objectUnderTest.deleteSchemaSet(DATASPACE_NAME, SCHEMA_SET_NAME_NO_ANCHORS,
-                    CASCADE_DELETE_PROHIBITED)
+            objectUnderTest.deleteSchemaSet(DATASPACE_NAME, SCHEMA_SET_NAME_NO_ANCHORS)
         then: 'the schema set has been deleted'
             schemaSetRepository.findByDataspaceAndName(dataspaceEntity, SCHEMA_SET_NAME_NO_ANCHORS).isPresent() == false
-        and: 'any orphaned (not used by any schema set anymore) yang resources are deleted'
-            def orphanedResourceId = 3100L
-            yangResourceRepository.findById(orphanedResourceId).isPresent() == false
-        and: 'any shared (still in use by other schema set) yang resources still persists'
-            def sharedResourceId = 3003L
-            yangResourceRepository.findById(sharedResourceId).isPresent()
-    }
-
-    @Sql([CLEAR_DATA, SET_DATA])
-    def 'Delete schema set with cascade allowed.'() {
-        when: 'a schema set is deleted with cascade-allowed option'
-            objectUnderTest.deleteSchemaSet(DATASPACE_NAME, SCHEMA_SET_NAME_WITH_ANCHORS_AND_DATA,
-                    CASCADE_DELETE_ALLOWED)
-        then: 'the schema set has been deleted'
-            schemaSetRepository
-                    .findByDataspaceAndName(dataspaceEntity, SCHEMA_SET_NAME_WITH_ANCHORS_AND_DATA).isPresent() == false
-        and: 'the associated anchors are removed'
-            def associatedAnchorsIds = [ 6001, 6002 ]
-            associatedAnchorsIds.each {anchorRepository.findById(it).isPresent() == false }
-        and: 'the fragment(s) under those anchors are removed'
-            def fragmentUnderAnchor1Id = 7001L
-            fragmentRepository.findById(fragmentUnderAnchor1Id).isPresent() == false
-        and: 'the shared resources still persist'
-            def sharedResourceIds = [ 3003L, 3004L ]
-            sharedResourceIds.each {yangResourceRepository.findById(it).isPresent() }
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
     def 'Delete schema set error scenario: #scenario.'() {
         when: 'attempt to delete a schema set where #scenario'
-            objectUnderTest.deleteSchemaSet(dataspaceName, schemaSetName, CASCADE_DELETE_PROHIBITED)
+            objectUnderTest.deleteSchemaSet(dataspaceName, schemaSetName)
         then: 'an #expectedException is thrown'
             thrown(expectedException)
         where: 'the following data is used'
             scenario                                   | dataspaceName  | schemaSetName                         || expectedException
             'dataspace does not exist'                 | 'unknown'      | 'not-relevant'                        || DataspaceNotFoundException
             'schema set does not exists'               | DATASPACE_NAME | 'unknown'                             || SchemaSetNotFoundException
-            'cascade prohibited but schema set in use' | DATASPACE_NAME | SCHEMA_SET_NAME_WITH_ANCHORS_AND_DATA || SchemaSetInUseException
+    }
+
+    @Sql([CLEAR_DATA, SET_DATA])
+    def 'Delete only orphan Yang Resources'() {
+        given: 'a schema set is deleted and and yang resource is not used anymore'
+            objectUnderTest.deleteSchemaSet(DATASPACE_NAME, SCHEMA_SET_NAME_NO_ANCHORS)
+        when: 'orphan yang resources are deleted'
+            objectUnderTest.deleteUnusedYangResourceModules()
+        then: 'any orphaned (not used by any schema set anymore) yang resources are deleted'
+            def orphanedResourceId = 3100L
+            yangResourceRepository.findById(orphanedResourceId).isPresent() == false
+        and: 'any shared (still in use by other schema set) yang resources still persists'
+            def sharedResourceId = 3003L
+            yangResourceRepository.findById(sharedResourceId).isPresent()
     }
 
     def assertSchemaSetPersisted(expectedDataspaceName,
index c9240f7..40fc44c 100644 (file)
@@ -2,7 +2,7 @@
    ============LICENSE_START=======================================================
     Copyright (C) 2020 Pantheon.tech
     Modifications Copyright (C) 2020 Nordix Foundation.
-    Modifications Copyright (C) 2021 Bell Canada.
+    Modifications Copyright (C) 2021-2022 Bell Canada.
    ================================================================================
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
 
 INSERT INTO DATASPACE (ID, NAME) VALUES
     (1001, 'DATASPACE-001'),
-    (1002, 'DATASPACE-002'),
+    (1002, 'DATASPACE-002-NO-DATA'),
     (1003, 'DATASPACE-003');
 
 INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
     (2001, 'SCHEMA-SET-001', 1001),
     (2002, 'SCHEMA-SET-002', 1001),
-    (2003, 'SCHEMA-SET-002', 1003);
+    (2003, 'SCHEMA-SET-002-NO-ANCHORS', 1003);
 
 INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
     (3001, 'ANCHOR-001', 1001, 2001),
index 7ba9599..44f7f77 100755 (executable)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Nordix Foundation
- *  Modifications Copyright (C) 2020 Bell Canada.
+ *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  Modifications Copyright (C) 2021 Pantheon.tech
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,7 +23,6 @@
 package org.onap.cps.api;
 
 import java.util.Collection;
-import org.checkerframework.checker.nullness.qual.NonNull;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.CpsException;
 import org.onap.cps.spi.model.Anchor;
@@ -39,14 +38,14 @@ public interface CpsAdminService {
      * @param dataspaceName dataspace name
      * @throws AlreadyDefinedException if dataspace with same name already exists
      */
-    void createDataspace(@NonNull String dataspaceName);
+    void createDataspace(String dataspaceName);
 
     /**
      * Delete dataspace.
      *
      * @param dataspaceName the name of the dataspace to delete
      */
-    void deleteDataspace(@NonNull String dataspaceName);
+    void deleteDataspace(String dataspaceName);
 
     /**
      * Create an Anchor.
@@ -56,7 +55,7 @@ public interface CpsAdminService {
      * @param anchorName    anchor name
      * @throws CpsException if input data is invalid.
      */
-    void createAnchor(@NonNull String dataspaceName, @NonNull String schemaSetName, @NonNull String anchorName);
+    void createAnchor(String dataspaceName, String schemaSetName, String anchorName);
 
     /**
      * Read all anchors in the given dataspace.
@@ -64,8 +63,16 @@ public interface CpsAdminService {
      * @param dataspaceName dataspace name
      * @return a collection of anchors
      */
-    @NonNull
-    Collection<Anchor> getAnchors(@NonNull String dataspaceName);
+    Collection<Anchor> getAnchors(String dataspaceName);
+
+    /**
+     * Read all anchors associated the given schema-set in the given dataspace.
+     *
+     * @param dataspaceName dataspace name
+     * @param schemaSetName schema-set name
+     * @return a collection of anchors
+     */
+    Collection<Anchor> getAnchors(String dataspaceName, String schemaSetName);
 
     /**
      * Get an anchor in the given dataspace using the anchor name.
@@ -74,8 +81,7 @@ public interface CpsAdminService {
      * @param anchorName    anchor name
      * @return an anchor
      */
-    @NonNull
-    Anchor getAnchor(@NonNull String dataspaceName, @NonNull String anchorName);
+    Anchor getAnchor(String dataspaceName, String anchorName);
 
     /**
      * Delete anchor by name in given dataspace.
@@ -83,14 +89,13 @@ public interface CpsAdminService {
      * @param dataspaceName dataspace name
      * @param anchorName    anchor name
      */
-    void deleteAnchor(@NonNull String dataspaceName, @NonNull String anchorName);
+    void deleteAnchor(String dataspaceName, String anchorName);
 
     /**
      * Query anchor names for the given module names in the provided dataspace.
      *
-     *
      * @param dataspaceName dataspace name
-     * @param moduleNames a collection of module names
+     * @param moduleNames   a collection of module names
      * @return a collection of anchor names in the given dataspace. The schema set for each anchor must include all the
      *         given module names
      */
index d831793..d30a657 100755 (executable)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Nordix Foundation
- *  Modifications Copyright (C) 2020 Bell Canada.
+ *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  Modifications Copyright (C) 2021 Pantheon.tech
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -56,6 +56,11 @@ public class CpsAdminServiceImpl implements CpsAdminService {
         return cpsAdminPersistenceService.getAnchors(dataspaceName);
     }
 
+    @Override
+    public Collection<Anchor> getAnchors(final String dataspaceName, final String schemaSetName) {
+        return cpsAdminPersistenceService.getAnchors(dataspaceName, schemaSetName);
+    }
+
     @Override
     public Anchor getAnchor(final String dataspaceName, final String anchorName) {
         return cpsAdminPersistenceService.getAnchor(dataspaceName, anchorName);
index 1032641..e967817 100644 (file)
@@ -2,6 +2,7 @@
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2020-2021 Nordix Foundation
  *  Modifications Copyright (C) 2020-2021 Pantheon.tech
+ *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -24,23 +25,38 @@ package org.onap.cps.api.impl;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import org.onap.cps.api.CpsAdminService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.spi.CascadeDeleteAllowed;
 import org.onap.cps.spi.CpsModulePersistenceService;
+import org.onap.cps.spi.exceptions.SchemaSetInUseException;
+import org.onap.cps.spi.model.Anchor;
 import org.onap.cps.spi.model.ModuleReference;
 import org.onap.cps.spi.model.SchemaSet;
 import org.onap.cps.yang.YangTextSchemaSourceSetBuilder;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 @Service("CpsModuleServiceImpl")
 public class CpsModuleServiceImpl implements CpsModuleService {
 
-    @Autowired
     private CpsModulePersistenceService cpsModulePersistenceService;
-
-    @Autowired
     private YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache;
+    private CpsAdminService cpsAdminService;
+
+    /**
+     * Create an instance of CpsModuleServiceImpl.
+     *
+     * @param cpsModulePersistenceService  cpsModulePersistenceService
+     * @param yangTextSchemaSourceSetCache yangTextSchemaSourceSetCache
+     * @param cpsAdminService              cpsAdminService
+     */
+    public CpsModuleServiceImpl(final CpsModulePersistenceService cpsModulePersistenceService,
+        final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache, final CpsAdminService cpsAdminService) {
+        this.cpsModulePersistenceService = cpsModulePersistenceService;
+        this.yangTextSchemaSourceSetCache = yangTextSchemaSourceSetCache;
+        this.cpsAdminService = cpsAdminService;
+    }
 
     @Override
     public void createSchemaSet(final String dataspaceName, final String schemaSetName,
@@ -53,10 +69,10 @@ public class CpsModuleServiceImpl implements CpsModuleService {
 
     @Override
     public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
-                                           final Map<String, String> newYangResourcesModuleNameToContentMap,
-                                           final List<ModuleReference> moduleReferences) {
+        final Map<String, String> newYangResourcesModuleNameToContentMap,
+        final List<ModuleReference> moduleReferences) {
         cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName,
-                newYangResourcesModuleNameToContentMap, moduleReferences);
+            newYangResourcesModuleNameToContentMap, moduleReferences);
 
     }
 
@@ -69,9 +85,18 @@ public class CpsModuleServiceImpl implements CpsModuleService {
     }
 
     @Override
+    @Transactional
     public void deleteSchemaSet(final String dataspaceName, final String schemaSetName,
         final CascadeDeleteAllowed cascadeDeleteAllowed) {
-        cpsModulePersistenceService.deleteSchemaSet(dataspaceName, schemaSetName, cascadeDeleteAllowed);
+        final Collection<Anchor> anchors = cpsAdminService.getAnchors(dataspaceName, schemaSetName);
+        if (!anchors.isEmpty() && isCascadeDeleteProhibited(cascadeDeleteAllowed)) {
+            throw new SchemaSetInUseException(dataspaceName, schemaSetName);
+        }
+        for (final Anchor anchor : anchors) {
+            cpsAdminService.deleteAnchor(dataspaceName, anchor.getName());
+        }
+        cpsModulePersistenceService.deleteSchemaSet(dataspaceName, schemaSetName);
+        cpsModulePersistenceService.deleteUnusedYangResourceModules();
     }
 
     @Override
@@ -84,4 +109,8 @@ public class CpsModuleServiceImpl implements CpsModuleService {
         final String anchorName) {
         return cpsModulePersistenceService.getYangResourceModuleReferences(dataspaceName, anchorName);
     }
+
+    private boolean isCascadeDeleteProhibited(final CascadeDeleteAllowed cascadeDeleteAllowed) {
+        return CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED == cascadeDeleteAllowed;
+    }
 }
index 9553700..dd4059d 100755 (executable)
@@ -1,7 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Nordix Foundation.
- *  Modifications Copyright (C) 2020 Bell Canada.
+ *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  Modifications Copyright (C) 2021 Pantheon.tech
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,7 +23,6 @@
 package org.onap.cps.spi;
 
 import java.util.Collection;
-import org.checkerframework.checker.nullness.qual.NonNull;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.model.Anchor;
 
@@ -38,14 +37,14 @@ public interface CpsAdminPersistenceService {
      * @param dataspaceName dataspace name
      * @throws AlreadyDefinedException if dataspace with same name already exists
      */
-    void createDataspace(@NonNull String dataspaceName);
+    void createDataspace(String dataspaceName);
 
     /**
      * Delete dataspace.
      *
      * @param dataspaceName the name of the dataspace to delete
      */
-    void deleteDataspace(@NonNull String dataspaceName);
+    void deleteDataspace(String dataspaceName);
 
     /**
      * Create an Anchor.
@@ -54,7 +53,16 @@ public interface CpsAdminPersistenceService {
      * @param schemaSetName schema set name
      * @param anchorName    anchor name
      */
-    void createAnchor(@NonNull String dataspaceName, @NonNull String schemaSetName, @NonNull String anchorName);
+    void createAnchor(String dataspaceName, String schemaSetName, String anchorName);
+
+    /**
+     * Read all anchors associated the given schema-set in the given dataspace.
+     *
+     * @param dataspaceName dataspace name
+     * @param schemaSetName schema-set name
+     * @return a collection of anchors
+     */
+    Collection<Anchor> getAnchors(String dataspaceName, String schemaSetName);
 
     /**
      * Read all anchors in the given a dataspace.
@@ -62,8 +70,7 @@ public interface CpsAdminPersistenceService {
      * @param dataspaceName dataspace name
      * @return a collection of anchors
      */
-    @NonNull
-    Collection<Anchor> getAnchors(@NonNull String dataspaceName);
+    Collection<Anchor> getAnchors(String dataspaceName);
 
     /**
      * Query anchor names for the given module names in the provided dataspace.
@@ -83,8 +90,7 @@ public interface CpsAdminPersistenceService {
      * @param anchorName anchor name
      * @return an anchor
      */
-    @NonNull
-    Anchor getAnchor(@NonNull String dataspaceName, @NonNull String anchorName);
+    Anchor getAnchor(String dataspaceName, String anchorName);
 
     /**
      * Delete anchor by name in given dataspace.
@@ -92,5 +98,5 @@ public interface CpsAdminPersistenceService {
      * @param dataspaceName dataspace name
      * @param anchorName anchor name
      */
-    void deleteAnchor(@NonNull String dataspaceName, @NonNull String anchorName);
+    void deleteAnchor(String dataspaceName, String anchorName);
 }
index 9b50f9e..e082734 100755 (executable)
@@ -1,7 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Nordix Foundation
- *  Modifications Copyright (C) 2020 Bell Canada.
+ *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -24,8 +24,6 @@ package org.onap.cps.spi;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import org.checkerframework.checker.nullness.qual.NonNull;
-import org.onap.cps.spi.exceptions.DataInUseException;
 import org.onap.cps.spi.model.ModuleReference;
 
 /**
@@ -40,8 +38,7 @@ public interface CpsModulePersistenceService {
      * @param schemaSetName                 schema set name
      * @param yangResourcesNameToContentMap YANG resources (files) map where key is a name and value is content
      */
-    void storeSchemaSet(@NonNull String dataspaceName, @NonNull String schemaSetName,
-        @NonNull Map<String, String> yangResourcesNameToContentMap);
+    void storeSchemaSet(String dataspaceName, String schemaSetName, Map<String, String> yangResourcesNameToContentMap);
 
     /**
      * Stores a schema set from new modules and existing modules.
@@ -49,45 +46,36 @@ public interface CpsModulePersistenceService {
      * @param dataspaceName                          Dataspace name
      * @param schemaSetName                          Schema set name
      * @param newYangResourcesModuleNameToContentMap YANG resources map where key is a module name and value is content
-     * @param moduleReferences                    List of YANG resources module references
+     * @param moduleReferences                       List of YANG resources module references
      */
-    void storeSchemaSetFromModules(@NonNull String dataspaceName, @NonNull String schemaSetName,
-                                   @NonNull Map<String, String> newYangResourcesModuleNameToContentMap,
-                                   @NonNull List<ModuleReference> moduleReferences);
+    void storeSchemaSetFromModules(String dataspaceName, String schemaSetName,
+        Map<String, String> newYangResourcesModuleNameToContentMap, List<ModuleReference> moduleReferences);
 
     /**
      * Deletes Schema Set.
      *
-     * @param dataspaceName        dataspace name
-     * @param schemaSetName        schema set name
-     * @param cascadeDeleteAllowed indicates the allowance to remove associated anchors and data if exist
-     * @throws DataInUseException if cascadeDeleteAllowed is set to CASCADE_DELETE_PROHIBITED and there
-     *                           is associated anchor record exists in database
+     * @param dataspaceName dataspace name
+     * @param schemaSetName schema set name
      */
-    void deleteSchemaSet(@NonNull String dataspaceName, @NonNull String schemaSetName,
-        @NonNull CascadeDeleteAllowed cascadeDeleteAllowed);
+    void deleteSchemaSet(String dataspaceName, String schemaSetName);
 
     /**
      * Returns YANG resources per specific dataspace / schemaSetName.
      *
-     * @param dataspaceName   dataspace name
+     * @param dataspaceName dataspace name
      * @param schemaSetName schema set name
      * @return YANG resources (files) map where key is a name and value is content
      */
-    @NonNull
-    Map<String, String> getYangSchemaResources(@NonNull String dataspaceName,
-        @NonNull String schemaSetName);
+    Map<String, String> getYangSchemaResources(String dataspaceName, String schemaSetName);
 
     /**
      * Returns YANG resources per specific dataspace / anchorName.
      *
      * @param dataspaceName dataspace name
-     * @param anchorName anchor name
+     * @param anchorName    anchor name
      * @return YANG resources (files) map where key is a name and value is content
      */
-    @NonNull
-    Map<String, String> getYangSchemaSetResources(@NonNull String dataspaceName,
-        @NonNull String anchorName);
+    Map<String, String> getYangSchemaSetResources(String dataspaceName, String anchorName);
 
     /**
      * Returns YANG resources module references for the given dataspace name.
@@ -105,4 +93,9 @@ public interface CpsModulePersistenceService {
      * @return a collection of module names and revisions
      */
     Collection<ModuleReference> getYangResourceModuleReferences(String dataspaceName, String anchorName);
+
+    /**
+     * Remove unused Yang Resource Modules.
+     */
+    void deleteUnusedYangResourceModules();
 }
index 6d1f586..fe6e460 100755 (executable)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Nordix Foundation
- *  Modifications Copyright (C) 2020 Bell Canada.
+ *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  Modifications Copyright (C) 2021 Pantheon.tech
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -56,6 +56,14 @@ class CpsAdminServiceImplSpec extends Specification {
             objectUnderTest.getAnchors('someDataspace') == anchors
     }
 
+    def 'Retrieve all anchors for schema-set.'() {
+        given: 'that anchor is associated with the dataspace and schemaset'
+            def anchors = [new Anchor()]
+            mockCpsAdminPersistenceService.getAnchors('someDataspace', 'someSchemaSet') >> anchors
+        expect: 'the collection provided by persistence service is returned as result'
+            objectUnderTest.getAnchors('someDataspace', 'someSchemaSet') == anchors
+    }
+
     def 'Retrieve anchor for dataspace and provided anchor name.'() {
         given: 'that anchor name is associated with the dataspace'
             Anchor anchor = new Anchor()
index 2c23aa1..b020570 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2020-2021 Nordix Foundation
  *  Modifications Copyright (C) 2020-2021 Pantheon.tech
- *  Modifications Copyright (C) 2020-2021 Bell Canada.
+ *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  ================================================================================
  *  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 org.onap.cps.TestUtils
+import org.onap.cps.api.CpsAdminService
 import org.onap.cps.spi.CpsModulePersistenceService
 import org.onap.cps.spi.exceptions.ModelValidationException
+import org.onap.cps.spi.exceptions.SchemaSetInUseException
+import org.onap.cps.spi.model.Anchor
 import org.onap.cps.spi.model.ExtendedModuleReference
 import org.onap.cps.spi.model.ModuleReference
 import org.spockframework.spring.SpringBean
@@ -35,18 +38,20 @@ import org.springframework.cache.annotation.EnableCaching
 import org.springframework.cache.caffeine.CaffeineCacheManager
 import org.springframework.test.context.ContextConfiguration
 import spock.lang.Specification
-
 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED
 
 @SpringBootTest
 @EnableCaching
-@ContextConfiguration(classes = [YangTextSchemaSourceSetCache.class, CpsModuleServiceImpl.class])
+@ContextConfiguration(classes = [YangTextSchemaSourceSetCache, CpsModuleServiceImpl])
 class CpsModuleServiceImplSpec extends Specification {
 
     @SpringBean
     CpsModulePersistenceService mockModuleStoreService = Mock()
 
+    @SpringBean
+    CpsAdminService mockCpsAdminService = Mock()
+
     @SpringBean
     CacheManager cacheManager = new CaffeineCacheManager("yangSchema")
 
@@ -105,18 +110,51 @@ class CpsModuleServiceImplSpec extends Specification {
             1 * mockModuleStoreService.getYangSchemaResources('someDataspace', 'someSchemaSet') >> yangResourcesNameToContentMap
     }
 
-    def 'Delete set by name and dataspace with #cascadeDeleteOption.'() {
-        when: 'schema set deletion is requested'
-            objectUnderTest.deleteSchemaSet(dataspaceName, schemaSetname, cascadeDeleteOption)
-        then: 'persistence service method is invoked with same parameters'
-            mockModuleStoreService.deleteSchemaSet(dataspaceName, schemaSetname, cascadeDeleteOption)
+    def 'Delete schema-set when cascade is allowed.'() {
+        given: '#numberOfAnchors anchors are associated with schemaset'
+            def associatedAnchors = createAnchors(numberOfAnchors)
+            mockCpsAdminService.getAnchors('my-dataspace', 'my-schemaset') >> associatedAnchors
+        when: 'schema set deletion is requested with cascade allowed'
+            objectUnderTest.deleteSchemaSet('my-dataspace', 'my-schemaset', CASCADE_DELETE_ALLOWED)
+        then: 'anchor deletion is called #numberOfAnchors times'
+            numberOfAnchors * mockCpsAdminService.deleteAnchor('my-dataspace', _)
+        and: 'persistence service method is invoked with same parameters'
+            1 * mockModuleStoreService.deleteSchemaSet('my-dataspace', 'my-schemaset')
+        and: 'orphan yang resources are deleted'
+            1 * mockModuleStoreService.deleteUnusedYangResourceModules()
         where: 'following parameters are used'
-            dataspaceName | schemaSetname   | cascadeDeleteOption
-            'dataspace-1' | 'schemas-set-1' | CASCADE_DELETE_ALLOWED
-            'dataspace-2' | 'schemas-set-2' | CASCADE_DELETE_PROHIBITED
+            numberOfAnchors << [0, 3]
+    }
+
+    def 'Delete schema-set when cascade is prohibited.'() {
+        given: 'no anchors are associated with schemaset'
+            mockCpsAdminService.getAnchors('my-dataspace', 'my-schemaset') >> Collections.emptyList()
+        when: 'schema set deletion is requested with cascade allowed'
+            objectUnderTest.deleteSchemaSet('my-dataspace', 'my-schemaset', CASCADE_DELETE_PROHIBITED)
+        then: 'no anchors are deleted'
+            0 * mockCpsAdminService.deleteAnchor(_, _)
+        and: 'persistence service method is invoked with same parameters'
+            1 * mockModuleStoreService.deleteSchemaSet('my-dataspace', 'my-schemaset')
+        and: 'orphan yang resources are deleted'
+            1 * mockModuleStoreService.deleteUnusedYangResourceModules()
+    }
+
+    def 'Delete schema-set when cascade is prohibited and schema-set has anchors.'() {
+        given: '2 anchors are associated with schemaset'
+            mockCpsAdminService.getAnchors('my-dataspace', 'my-schemaset') >> createAnchors(2)
+        when: 'schema set deletion is requested with cascade allowed'
+            objectUnderTest.deleteSchemaSet('my-dataspace', 'my-schemaset', CASCADE_DELETE_PROHIBITED)
+        then: 'Schema-Set in Use exception is thrown'
+            thrown(SchemaSetInUseException)
+    }
+
+    def createAnchors(int anchorCount) {
+        def anchors = []
+        (0..<anchorCount).each { anchors.add(new Anchor("my-anchor-$it", 'my-dataspace', 'my-schemaset')) }
+        return anchors
     }
 
-    def 'Get all yang resources module references.'(){
+    def 'Get all yang resources module references.'() {
         given: 'an already present module reference'
             def moduleReferences = [new ExtendedModuleReference()]
             mockModuleStoreService.getYangResourceModuleReferences('someDataspaceName') >> moduleReferences
@@ -125,7 +163,7 @@ class CpsModuleServiceImplSpec extends Specification {
     }
 
 
-    def 'Get all yang resources module references for the given dataspace name and anchor name.'(){
+    def 'Get all yang resources module references for the given dataspace name and anchor name.'() {
         given: 'the module store service service returns a list module references'
             def moduleReferences = [new ModuleReference()]
             mockModuleStoreService.getYangResourceModuleReferences('someDataspaceName', 'someAnchorName') >> moduleReferences
index eefa86e..d18bcf5 100755 (executable)
@@ -1,7 +1,7 @@
 /*\r
  * ============LICENSE_START=======================================================\r
  * Copyright (C) 2021 Nordix Foundation.\r
- * Modifications Copyright (C) 2021 Bell Canada.\r
+ * Modifications Copyright (C) 2021-2022 Bell Canada.\r
  * Modifications Copyright (C) 2021 Pantheon.tech\r
  * ================================================================================\r
  * Licensed under the Apache License, Version 2.0 (the "License");\r
@@ -22,7 +22,6 @@
 \r
 package org.onap.cps.api.impl\r
 \r
-import java.time.OffsetDateTime\r
 import org.onap.cps.TestUtils\r
 import org.onap.cps.api.CpsAdminService\r
 import org.onap.cps.notification.NotificationService\r
@@ -38,9 +37,10 @@ class E2ENetworkSliceSpec extends Specification {
     def mockDataStoreService = Mock(CpsDataPersistenceService)\r
     def mockCpsAdminService = Mock(CpsAdminService)\r
     def mockNotificationService = Mock(NotificationService)\r
-    def cpsModuleServiceImpl = new CpsModuleServiceImpl()\r
     def cpsDataServiceImpl = new CpsDataServiceImpl()\r
     def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)\r
+    def cpsModuleServiceImpl = new CpsModuleServiceImpl(mockModuleStoreService,\r
+            mockYangTextSchemaSourceSetCache,mockCpsAdminService )\r
 \r
     def dataspaceName = 'someDataspace'\r
     def anchorName = 'someAnchor'\r
@@ -52,8 +52,6 @@ class E2ENetworkSliceSpec extends Specification {
         cpsDataServiceImpl.cpsAdminService = mockCpsAdminService\r
         cpsDataServiceImpl.yangTextSchemaSourceSetCache = mockYangTextSchemaSourceSetCache\r
         cpsDataServiceImpl.notificationService = mockNotificationService\r
-        cpsModuleServiceImpl.yangTextSchemaSourceSetCache = mockYangTextSchemaSourceSetCache\r
-        cpsModuleServiceImpl.cpsModulePersistenceService = mockModuleStoreService\r
     }\r
 \r
     def 'E2E model can be parsed by CPS.'() {\r