diff options
13 files changed, 236 insertions, 136 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java index 9c69006ece..51b2482953 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java @@ -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 diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java index e0f54265ab..3e39a05c51 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java @@ -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. */ diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy index 4b5b116f48..2218014ec1 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy @@ -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)' } } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy index a223e7135f..75d6330265 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy @@ -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, diff --git a/cps-ri/src/test/resources/data/anchor.sql b/cps-ri/src/test/resources/data/anchor.sql index c9240f7fd2..40fc44c0ae 100644 --- a/cps-ri/src/test/resources/data/anchor.sql +++ b/cps-ri/src/test/resources/data/anchor.sql @@ -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. @@ -22,13 +22,13 @@ 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), diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java b/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java index 7ba95995a5..44f7f77152 100755 --- a/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java @@ -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 */ diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java index d831793264..d30a6571d8 100755 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java @@ -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"); @@ -57,6 +57,11 @@ public class CpsAdminServiceImpl implements CpsAdminService { } @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); } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java index 10326413c3..e967817867 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java @@ -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; + } } diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java index 95537006a7..dd4059d88c 100755 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java @@ -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); } diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java index 9b50f9e917..e082734417 100755 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java @@ -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(); } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy index 6d1f586295..fe6e460862 100755 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy @@ -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() diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy index 2c23aa1bc8..b0205705a7 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy @@ -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. @@ -23,8 +23,11 @@ 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,19 +38,21 @@ 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") @Autowired @@ -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 diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy index eefa86e903..d18bcf55ef 100755 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy @@ -1,7 +1,7 @@ /*
* ============LICENSE_START=======================================================
* Copyright (C) 2021 Nordix Foundation.
- * Modifications Copyright (C) 2021 Bell Canada.
+ * Modifications Copyright (C) 2021-2022 Bell Canada.
* Modifications Copyright (C) 2021 Pantheon.tech
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,6 @@ package org.onap.cps.api.impl
-import java.time.OffsetDateTime
import org.onap.cps.TestUtils
import org.onap.cps.api.CpsAdminService
import org.onap.cps.notification.NotificationService
@@ -38,9 +37,10 @@ class E2ENetworkSliceSpec extends Specification { def mockDataStoreService = Mock(CpsDataPersistenceService)
def mockCpsAdminService = Mock(CpsAdminService)
def mockNotificationService = Mock(NotificationService)
- def cpsModuleServiceImpl = new CpsModuleServiceImpl()
def cpsDataServiceImpl = new CpsDataServiceImpl()
def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
+ def cpsModuleServiceImpl = new CpsModuleServiceImpl(mockModuleStoreService,
+ mockYangTextSchemaSourceSetCache,mockCpsAdminService )
def dataspaceName = 'someDataspace'
def anchorName = 'someAnchor'
@@ -52,8 +52,6 @@ class E2ENetworkSliceSpec extends Specification { cpsDataServiceImpl.cpsAdminService = mockCpsAdminService
cpsDataServiceImpl.yangTextSchemaSourceSetCache = mockYangTextSchemaSourceSetCache
cpsDataServiceImpl.notificationService = mockNotificationService
- cpsModuleServiceImpl.yangTextSchemaSourceSetCache = mockYangTextSchemaSourceSetCache
- cpsModuleServiceImpl.cpsModulePersistenceService = mockModuleStoreService
}
def 'E2E model can be parsed by CPS.'() {
|