Merge "Test Scenario for replaceListContent"
authoraditya puthuparambil <aditya.puthuparambil@bell.ca>
Thu, 3 Feb 2022 11:09:26 +0000 (11:09 +0000)
committerGerrit Code Review <gerrit@onap.org>
Thu, 3 Feb 2022 11:09:26 +0000 (11:09 +0000)
24 files changed:
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmp-inventory.yml
cps-ncmp-rest/docs/openapi/ncmp.yml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiRequestException.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/ServerNcmpException.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
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 021a790..22453f3 100644 (file)
@@ -382,3 +382,13 @@ components:
     NoContent:
       description: No Content
       content: {}
+    InternalServerError:
+      description: Internal Server Error
+      content:
+        application/json:
+          schema:
+            $ref: "#/components/schemas/ErrorMessage"
+          example:
+            status: 500
+            message: Internal Server Error
+            details: Internal Server Error occurred
index b0a50aa..f3f84fe 100755 (executable)
@@ -1,5 +1,6 @@
 #  ============LICENSE_START=======================================================
 #  Copyright (C) 2021 Bell Canada
+#  Modifications Copyright (C) 2021-2022 Nordix Foundation
 #  ================================================================================
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -38,3 +39,5 @@ updateDmiRegistration:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
index d80ec65..3a71aba 100755 (executable)
@@ -45,8 +45,8 @@ getResourceDataForPassthroughOperational:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
 resourceDataForPassthroughRunning:
   get:
@@ -76,8 +76,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
   post:
     tags:
       - network-cm-proxy
@@ -112,8 +112,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
   put:
     tags:
@@ -149,8 +149,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
   patch:
     tags:
@@ -180,8 +180,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
   delete:
     tags:
@@ -204,7 +204,8 @@ resourceDataForPassthroughRunning:
         $ref: 'components.yaml#/components/responses/Forbidden'
       404:
         $ref: 'components.yaml#/components/responses/NotFound'
-
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
 fetchModuleReferencesByCmHandle:
   get:
@@ -230,8 +231,8 @@ fetchModuleReferencesByCmHandle:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
 
 executeCmHandleSearch:
   post:
@@ -259,3 +260,5 @@ executeCmHandleSearch:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
index ce9dd5a..8c6f9f1 100755 (executable)
@@ -21,7 +21,9 @@
 package org.onap.cps.ncmp.rest.exceptions;
 
 import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.impl.exception.DmiRequestException;
 import org.onap.cps.ncmp.api.impl.exception.NcmpException;
+import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException;
 import org.onap.cps.ncmp.rest.controller.NetworkCmProxyController;
 import org.onap.cps.ncmp.rest.model.ErrorMessage;
 import org.onap.cps.spi.exceptions.CpsException;
@@ -56,11 +58,16 @@ public class NetworkCmProxyRestExceptionHandler {
         return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception);
     }
 
-    @ExceptionHandler({NcmpException.class})
-    public static ResponseEntity<Object> handleNcmpExceptions(final NcmpException exception) {
+    @ExceptionHandler({ServerNcmpException.class})
+    public static ResponseEntity<Object> handleServerNcmpExceptions(final ServerNcmpException exception) {
         return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception);
     }
 
+    @ExceptionHandler({DmiRequestException.class})
+    public static ResponseEntity<Object> handleDmiRequestExceptions(final DmiRequestException exception) {
+        return buildErrorResponse(HttpStatus.BAD_REQUEST, exception);
+    }
+
     private static ResponseEntity<Object> buildErrorResponse(final HttpStatus status, final Exception exception) {
         if (exception.getCause() != null || !(exception instanceof CpsException)) {
             log.error("Exception occurred", exception);
index 7b3cd89..1b72b8c 100644 (file)
@@ -23,7 +23,8 @@ package org.onap.cps.ncmp.rest.exceptions
 import groovy.json.JsonSlurper
 import org.modelmapper.ModelMapper
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
-import org.onap.cps.ncmp.api.impl.exception.NcmpException
+import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
+import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
 import org.onap.cps.spi.exceptions.CpsException
 import org.onap.cps.utils.JsonObjectMapper
 import org.spockframework.spring.SpringBean
@@ -34,6 +35,7 @@ import org.springframework.test.web.servlet.MockMvc
 import spock.lang.Shared
 import spock.lang.Specification
 
+import static org.springframework.http.HttpStatus.BAD_REQUEST
 import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
 
@@ -66,17 +68,18 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
         dataNodeBaseEndpoint = "$basePath/v1"
     }
 
-    def 'Get request with generic #scenario exception returns HTTP Status Internal Server Error.'() {
-        when: 'generic CPS exception is thrown by the service'
+    def 'Get request with generic #scenario exception returns correct HTTP Status.'() {
+        when: 'an exception is thrown by the service'
             setupTestException(exception)
             def response = performTestRequest()
-        then: 'an HTTP Internal Server Error response is returned with correct message and details'
-            assertTestResponse(response, INTERNAL_SERVER_ERROR, errorMessage, expectedErrorDetails)
+        then: 'an HTTP response is returned with correct message and details'
+            assertTestResponse(response, expectedErrorCode, errorMessage, expectedErrorDetails)
         where:
-            scenario | exception                                     || expectedErrorDetails
-            'CPS'    | new CpsException(errorMessage, errorDetails)  || errorDetails
-            'NCMP'   | new NcmpException(errorMessage, errorDetails) || null
-            'other'  | new IllegalStateException(errorMessage)       || null
+            scenario      | exception                                           || expectedErrorDetails | expectedErrorCode
+            'CPS'         | new CpsException(errorMessage, errorDetails)        || errorDetails         | INTERNAL_SERVER_ERROR
+            'NCMP-server' | new ServerNcmpException(errorMessage, errorDetails) || null                 | INTERNAL_SERVER_ERROR
+            'NCMP-client' | new DmiRequestException(errorMessage, errorDetails) || null                 | BAD_REQUEST
+            'other'       | new IllegalStateException(errorMessage)             || null                 | INTERNAL_SERVER_ERROR
     }
 
     def setupTestException(exception){
index 82145ef..6a156b8 100755 (executable)
@@ -39,7 +39,7 @@ import org.onap.cps.api.CpsAdminService;
 import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.api.impl.exception.NcmpException;
+import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException;
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations;
 import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
@@ -170,7 +170,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         if (responseEntity.getStatusCode().is2xxSuccessful()) {
             return responseEntity.getBody();
         } else {
-            throw new NcmpException(exceptionMessage,
+            throw new ServerNcmpException(exceptionMessage,
                     "DMI status code: " + responseEntity.getStatusCodeValue()
                             + ", DMI response body: " + responseEntity.getBody());
         }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiRequestException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiRequestException.java
new file mode 100644 (file)
index 0000000..d41ca70
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 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.ncmp.api.impl.exception;
+
+/**
+ * Client Based Network CM Proxy exception.
+ */
+public class DmiRequestException extends NcmpException {
+
+    /**
+     * Constructor.
+     *
+     * @param message the error message
+     * @param details the error details
+     */
+    public DmiRequestException(final String message, final String details) {
+        super(message, details);
+    }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/ServerNcmpException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/ServerNcmpException.java
new file mode 100644 (file)
index 0000000..a03a2d3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 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.ncmp.api.impl.exception;
+
+/**
+ * Server Based Network CM Proxy exception.
+ */
+public class ServerNcmpException extends NcmpException {
+
+    /**
+     * Constructor.
+     *
+     * @param message the error message
+     * @param details the error details
+     */
+    public ServerNcmpException(final String message, final String details) {
+        super(message, details);
+    }
+}
index 9faf733..f1b3888 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021 Nordix Foundation
+ *  Copyright (C) 2021-2022 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ import com.google.common.base.Strings;
 import java.util.List;
 import lombok.Getter;
 import lombok.Setter;
+import org.onap.cps.ncmp.api.impl.exception.DmiRequestException;
 import org.onap.cps.ncmp.api.impl.exception.NcmpException;
 
 /**
@@ -80,7 +81,7 @@ public class DmiPluginRegistration {
         }
 
         if (errorMessage != null) {
-            throw new NcmpException(errorMessage, "Please supply correct plugin information.");
+            throw new DmiRequestException(errorMessage, "Please supply correct plugin information.");
         }
     }
 
index 3ec6f3a..9f1203d 100644 (file)
@@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
+import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
 import org.onap.cps.ncmp.api.impl.exception.NcmpException
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
@@ -176,8 +177,8 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             dmiPluginRegistration.createdCmHandles = [persistenceCmHandle]
         when: 'registration is called with incorrect DMI plugin information'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'an NcmpException is thrown with correct message details'
-            def exceptionThrown = thrown(NcmpException)
+        then: 'a DMI Request Exception is thrown with correct message details'
+            def exceptionThrown = thrown(DmiRequestException.class)
             assert exceptionThrown.getMessage().contains(expectedMessageDetails)
         and: 'registration is not called'
             0 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)
index 7f12700..9c79d4f 100644 (file)
@@ -35,7 +35,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
-import org.onap.cps.ncmp.api.impl.exception.NcmpException
+import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.model.DataNode
@@ -88,12 +88,11 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'testResourceId', CREATE,
                 '{some-json}', 'application/json')
         then: 'exception is thrown'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
         and: 'details contains (not found) error code: 404'
             exceptionThrown.details.contains('404')
     }
 
-
     def 'Get resource data for pass-through operational from DMI.'() {
         given: 'get data node is called'
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
@@ -129,7 +128,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'testAcceptParam',
                 '(a=1,b=2)')
         then: 'exception is thrown with the expected details'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
             exceptionThrown.details == 'DMI status code: 404, DMI response body: NOK-json'
     }
 
@@ -150,7 +149,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'testAcceptParam',
                 '(a=1,b=2)')
         then: 'exception is thrown'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
         and: 'details contains the original response'
             exceptionThrown.details.contains('NOK-json')
     }
@@ -191,7 +190,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 'testAcceptParam',
                 '(a=1,b=2)')
         then: 'exception is thrown'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
         and: 'details contains the original response'
             exceptionThrown.details.contains('NOK-json')
     }
@@ -236,7 +235,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 '{some-json}',
                 'application/json')
         then: 'an exception is thrown with the expected error message detailsd with correct operation'
-            def exceptionThrown = thrown(NcmpException.class)
+            def exceptionThrown = thrown(ServerNcmpException.class)
             exceptionThrown.getMessage().contains(expectedResponseMessage)
         where:
             scenario | givenOperation || expectedResponseMessage
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