diff options
6 files changed, 304 insertions, 366 deletions
diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml index 3ef57cf03a..db274f7f01 100644 --- a/cps-ri/pom.xml +++ b/cps-ri/pom.xml @@ -33,8 +33,8 @@ <artifactId>cps-ri</artifactId>
<properties>
- <minimum-coverage>0.45</minimum-coverage>
- <!-- Coverage is provided by integration-test module instead -->
+ <minimum-coverage>0.64</minimum-coverage>
+ <!-- Additional coverage is provided by integration-test module -->
</properties>
<dependencies>
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 cd1457e359..5eda15a3d7 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 @@ -37,6 +37,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.transaction.Transactional; @@ -94,20 +95,14 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ @Override public Map<String, String> getYangSchemaResources(final String dataspaceName, final String schemaSetName) { - final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final var schemaSetEntity = + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); + final SchemaSetEntity schemaSetEntity = schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName); return schemaSetEntity.getYangResources().stream().collect( Collectors.toMap(YangResourceEntity::getFileName, YangResourceEntity::getContent)); } @Override - public Map<String, String> getYangSchemaSetResources(final String dataspaceName, final String anchorName) { - final var anchor = cpsAdminPersistenceService.getAnchor(dataspaceName, anchorName); - return getYangSchemaResources(dataspaceName, anchor.getSchemaSetName()); - } - - @Override public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName) { final Set<YangResourceModuleReference> yangResourceModuleReferenceList = yangResourceRepository.findAllModuleReferencesByDataspace(dataspaceName); @@ -143,9 +138,9 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ @Backoff(random = true, delay = 200, maxDelay = 2000, multiplier = 2)) public void storeSchemaSet(final String dataspaceName, final String schemaSetName, final Map<String, String> moduleReferenceNameToContentMap) { - final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final var yangResourceEntities = synchronizeYangResources(moduleReferenceNameToContentMap); - final var schemaSetEntity = new SchemaSetEntity(); + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); + final Set<YangResourceEntity> yangResourceEntities = synchronizeYangResources(moduleReferenceNameToContentMap); + final SchemaSetEntity schemaSetEntity = new SchemaSetEntity(); schemaSetEntity.setName(schemaSetName); schemaSetEntity.setDataspace(dataspaceEntity); schemaSetEntity.setYangResources(yangResourceEntities); @@ -185,8 +180,8 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ @Override @Transactional public void deleteSchemaSet(final String dataspaceName, final String schemaSetName) { - final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final var schemaSetEntity = + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); + final SchemaSetEntity schemaSetEntity = schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName); schemaSetRepository.delete(schemaSetEntity); } @@ -194,7 +189,7 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ @Override @Transactional public void deleteSchemaSets(final String dataspaceName, final Collection<String> schemaSetNames) { - final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName); + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); schemaSetRepository.deleteByDataspaceAndNameIn(dataspaceEntity, schemaSetNames); } @@ -217,7 +212,7 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ final String checksum = DigestUtils.sha256Hex(entry.getValue().getBytes(StandardCharsets.UTF_8)); final Map<String, String> moduleNameAndRevisionMap = createModuleNameAndRevisionMap(entry.getKey(), entry.getValue()); - final var yangResourceEntity = new YangResourceEntity(); + final YangResourceEntity yangResourceEntity = new YangResourceEntity(); yangResourceEntity.setFileName(entry.getKey()); yangResourceEntity.setContent(entry.getValue()); yangResourceEntity.setModuleName(moduleNameAndRevisionMap.get("moduleName")); @@ -264,10 +259,10 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ private static Map<String, String> createModuleNameAndRevisionMap(final String sourceName, final String source) { final Map<String, String> metaDataMap = new HashMap<>(); - final var revisionSourceIdentifier = - createIdentifierFromSourceName(checkNotNull(sourceName)); + final RevisionSourceIdentifier revisionSourceIdentifier = + createIdentifierFromSourceName(checkNotNull(sourceName)); - final var tempYangTextSchemaSource = new YangTextSchemaSource(revisionSourceIdentifier) { + final YangTextSchemaSource tempYangTextSchemaSource = new YangTextSchemaSource(revisionSourceIdentifier) { @Override public Optional<String> getSymbolicName() { return Optional.empty(); @@ -285,9 +280,10 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ } }; try { - final var dependencyInfo = YangModelDependencyInfo.forYangText(tempYangTextSchemaSource); - metaDataMap.put("moduleName", dependencyInfo.getName()); - metaDataMap.put("revision", dependencyInfo.getFormattedRevision()); + final YangModelDependencyInfo yangModelDependencyInfo + = YangModelDependencyInfo.forYangText(tempYangTextSchemaSource); + metaDataMap.put("moduleName", yangModelDependencyInfo.getName()); + metaDataMap.put("revision", yangModelDependencyInfo.getFormattedRevision()); } catch (final YangSyntaxErrorException | IOException e) { throw new ModelValidationException("Yang resource is invalid.", String.format("Yang syntax validation failed for resource %s:%n%s", sourceName, e.getMessage()), e); @@ -296,7 +292,7 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ } private static RevisionSourceIdentifier createIdentifierFromSourceName(final String sourceName) { - final var matcher = RFC6020_RECOMMENDED_FILENAME_PATTERN.matcher(sourceName); + final Matcher matcher = RFC6020_RECOMMENDED_FILENAME_PATTERN.matcher(sourceName); if (matcher.matches()) { return RevisionSourceIdentifier.create(matcher.group(1), Revision.of(matcher.group(2))); } @@ -335,15 +331,8 @@ 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. - */ - private String getNameForChecksum( - final String checksum, final Collection<YangResourceEntity> yangResourceEntities) { + private String getNameForChecksum(final String checksum, + final Collection<YangResourceEntity> yangResourceEntities) { final Optional<String> optionalFileName = yangResourceEntities.stream() .filter(entity -> StringUtils.equals(checksum, (entity.getChecksum()))) .findFirst() @@ -354,19 +343,12 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ return null; } - /** - * 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. - */ private String getDuplicatedChecksumFromException(final ConstraintViolationException exception) { - String checksum = null; - final var matcher = CHECKSUM_EXCEPTION_PATTERN.matcher(exception.getSQLException().getMessage()); + final Matcher matcher = CHECKSUM_EXCEPTION_PATTERN.matcher(exception.getSQLException().getMessage()); if (matcher.find() && matcher.groupCount() == 1) { - checksum = matcher.group(1); + return matcher.group(1); } - return checksum; + return null; } private static ModuleReference toModuleReference( 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 deleted file mode 100644 index 3a5d9ef769..0000000000 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy +++ /dev/null @@ -1,313 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation - * Modifications Copyright (C) 2021-2022 Bell Canada. - * Modifications Copyright (C) 2022 TechMahindra Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.cps.spi.impl - -import org.onap.cps.spi.CpsAdminPersistenceService -import org.onap.cps.spi.CpsModulePersistenceService -import org.onap.cps.spi.entities.YangResourceEntity -import org.onap.cps.spi.exceptions.AlreadyDefinedException -import org.onap.cps.spi.exceptions.DataspaceNotFoundException -import org.onap.cps.spi.exceptions.SchemaSetNotFoundException -import org.onap.cps.spi.model.ModuleDefinition -import org.onap.cps.spi.model.ModuleReference -import org.onap.cps.spi.model.SchemaSet -import org.onap.cps.spi.repository.SchemaSetRepository -import org.onap.cps.spi.repository.SchemaSetYangResourceRepositoryImpl -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.test.context.jdbc.Sql - -class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { - - @Autowired - CpsModulePersistenceService objectUnderTest - - @Autowired - SchemaSetRepository schemaSetRepository - - @Autowired - CpsAdminPersistenceService cpsAdminPersistenceService - - final static String SET_DATA = '/data/schemaset.sql' - - def static EXISTING_SCHEMA_SET_NAME = SCHEMA_SET_NAME1 - def SCHEMA_SET_NAME_NO_ANCHORS = 'SCHEMA-SET-100' - def NEW_SCHEMA_SET_NAME = 'SCHEMA-SET-NEW' - def NEW_RESOURCE_NAME = 'some new resource' - def NEW_RESOURCE_CONTENT = 'module stores {\n' + - ' yang-version 1.1;\n' + - ' namespace "org:onap:ccsdk:sample";\n' + - '\n' + - ' prefix book-store;\n' + - '\n' + - ' revision "2020-09-15" {\n' + - ' description\n' + - ' "Sample Model";\n' + - ' }' + - '}' - def NEW_RESOURCE_CHECKSUM = 'b13faef573ed1374139d02c40d8ce09c80ea1dc70e63e464c1ed61568d48d539' - def NEW_RESOURCE_MODULE_NAME = 'stores' - def NEW_RESOURCE_REVISION = '2020-09-15' - def newYangResourcesNameToContentMap = [(NEW_RESOURCE_NAME):NEW_RESOURCE_CONTENT] - - def dataspaceEntity - - def setup() { - dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME) - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Getting yang resource ids from module references'() { - when: 'getting yang resources for #scenario' - def result = yangResourceRepository.getResourceIdsByModuleReferences(moduleReferences) - then: 'the result contains the expected number entries' - assert result.size() == expectedResultSize - where: 'the following module references are provided' - scenario | moduleReferences || expectedResultSize - '2 valid module references' | [ new ModuleReference('MODULE-NAME-002','REVISION-002'), new ModuleReference('MODULE-NAME-003','REVISION-002') ] || 2 - '1 invalid module reference' | [ new ModuleReference('NOT EXIST','IRRELEVANT') ] || 0 - '1 valid and 1 invalid module reference' | [ new ModuleReference('MODULE-NAME-002','REVISION-002'), new ModuleReference('NOT EXIST','IRRELEVANT') ] || 1 - 'no module references' | [] || 0 - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Store schema set error scenario: #scenario.'() { - when: 'attempt to store schema set #schemaSetName in dataspace #dataspaceName' - objectUnderTest.storeSchemaSet(dataspaceName, schemaSetName, newYangResourcesNameToContentMap) - 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 already exists' | DATASPACE_NAME | EXISTING_SCHEMA_SET_NAME || AlreadyDefinedException - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Store new schema set with one module'() { - when: 'a new schema set with one module is stored' - objectUnderTest.storeSchemaSet(DATASPACE_NAME, NEW_SCHEMA_SET_NAME, newYangResourcesNameToContentMap) - then: 'the schema set is persisted correctly' - assertSchemaSetWithOneModuleIsPersistedCorrectly(NEW_RESOURCE_NAME, - NEW_RESOURCE_MODULE_NAME, NEW_RESOURCE_REVISION, NEW_RESOURCE_CONTENT, NEW_RESOURCE_CHECKSUM) - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Store new schema set with multiple modules.'() { - given: 'a new schema set with #numberOfModules modules' - def numberOfModules = 2 - String schemaSetName = "NewSchemaWith${numberOfModules}Modules" - def newYangResourcesNameToContentMap = [:] - (1..numberOfModules).each { - def uniqueRevision = String.valueOf(2000 + it) + '-01-01' - def uniqueContent = NEW_RESOURCE_CONTENT.replace(NEW_RESOURCE_REVISION, uniqueRevision) - newYangResourcesNameToContentMap.put(uniqueRevision, uniqueContent) - } - when: 'the new schema set is stored' - objectUnderTest.storeSchemaSet(DATASPACE_NAME, schemaSetName, newYangResourcesNameToContentMap) - then: 'the correct number of modules are persisted' - assert getYangResourceEntities(schemaSetName).size() == numberOfModules - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Store and retrieve new schema set from new modules and existing modules.'() { - given: 'a new module' - def mapOfNewModules = [newModule: 'module newmodule { yang-version 1.1; revision "2022-08-19" { } }'] - and: 'there are more existing modules in the db than the batch size (100)' - def listOfExistingModulesModuleReference = [] - def mapOfExistingModule = [:] - def numberOfModules = 1 + SchemaSetYangResourceRepositoryImpl.MAX_INSERT_BATCH_SIZE - (1..numberOfModules).each { - def uniqueRevision = String.valueOf(2000 + it) + '-01-01' - def uniqueContent = "module test { yang-version 1.1; revision \"${uniqueRevision}\" { } }".toString() - mapOfNewModules.put( 'test' + it, uniqueContent) - listOfExistingModulesModuleReference.add(new ModuleReference('test',uniqueRevision)) - } - objectUnderTest.storeSchemaSet(DATASPACE_NAME, 'existing schema set ', mapOfExistingModule) - when: 'a new schema set is created from these new modules and existing modules' - objectUnderTest.storeSchemaSetFromModules(DATASPACE_NAME, NEW_SCHEMA_SET_NAME , mapOfNewModules, listOfExistingModulesModuleReference) - then: 'the schema set can be retrieved' - def actualYangResourcesMapAfterSchemaSetHasBeenCreated = - objectUnderTest.getYangSchemaResources(DATASPACE_NAME, NEW_SCHEMA_SET_NAME) - and: 'it has all the new and existing modules' - def expectedYangResourcesMapAfterSchemaSetHasBeenCreated = mapOfNewModules + mapOfExistingModule - assert actualYangResourcesMapAfterSchemaSetHasBeenCreated == expectedYangResourcesMapAfterSchemaSetHasBeenCreated - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Retrieving schema set (resources) by anchor.'() { - given: 'a new schema set is stored' - objectUnderTest.storeSchemaSet(DATASPACE_NAME, NEW_SCHEMA_SET_NAME, newYangResourcesNameToContentMap) - and: 'an anchor is created with that schema set' - cpsAdminPersistenceService.createAnchor(DATASPACE_NAME, NEW_SCHEMA_SET_NAME, ANCHOR_NAME1) - when: 'the schema set resources for that anchor is retrieved' - def result = objectUnderTest.getYangSchemaSetResources(DATASPACE_NAME, ANCHOR_NAME1) - then: 'the correct resources are returned' - result == newYangResourcesNameToContentMap - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Retrieving all yang resources module references for the given dataspace.'() { - given: 'a dataspace name' - def dataspaceName = 'DATASPACE-002' - when: 'all yang resources module references are retrieved for the given dataspace name' - def result = objectUnderTest.getYangResourceModuleReferences(dataspaceName) - then: 'the correct resources are returned' - result.sort() == [new ModuleReference(moduleName: 'MODULE-NAME-005', revision: 'REVISION-002'), - new ModuleReference(moduleName: 'MODULE-NAME-006', revision: 'REVISION-006')] - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Retrieving module names and revisions for the given anchor.'() { - given: 'a dataspace name and anchor name' - def dataspaceName = 'DATASPACE-001' - def anchorName = 'ANCHOR1' - when: 'all yang resources module references are retrieved for the given anchor' - def result = objectUnderTest.getYangResourceModuleReferences(dataspaceName, anchorName) - then: 'the correct module names and revisions are returned' - result.sort() == [ new ModuleReference(moduleName: 'MODULE-NAME-003', revision: 'REVISION-002'), - new ModuleReference(moduleName: 'MODULE-NAME-004', revision: 'REVISION-004')] - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Storing duplicate schema content.'() { - given: 'a new schema set with a resource with the same content as an existing resource' - def existingResourceContent = 'module test { yang-version 1.1; revision "2020-09-15"; }' - def newYangResourcesNameToContentMap = [(NEW_RESOURCE_NAME):existingResourceContent] - when: 'the schema set with duplicate resource is stored' - objectUnderTest.storeSchemaSet(DATASPACE_NAME, NEW_SCHEMA_SET_NAME, newYangResourcesNameToContentMap) - then: 'the schema persisted has the new name and has the same checksum' - def existingResourceChecksum = 'bea1afcc3d1517e7bf8cae151b3b6bfbd46db77a81754acdcb776a50368efa0a' - def existingResourceModuleName = 'test' - def existingResourceRevision = '2020-09-15' - assertSchemaSetWithOneModuleIsPersistedCorrectly(NEW_RESOURCE_NAME, existingResourceModuleName, - existingResourceRevision, existingResourceContent, existingResourceChecksum) - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Retrieve schema sets for a given dataspace name'() { - when: 'the schema set resources for a given dataspace name is retrieved' - def result = objectUnderTest.getSchemaSetsByDataspaceName(DATASPACE_NAME) - then: 'the correct resources are returned' - result.contains(new SchemaSet(name: 'SCHEMA-SET-001', dataspaceName: 'DATASPACE-001')) - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Delete schema set'() { - when: 'a schema set is deleted with cascade-prohibited option' - 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() - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Delete schema sets'() { - when: 'schema sets are deleted' - objectUnderTest.deleteSchemaSets(DATASPACE_NAME, ['SCHEMA-SET-001', 'SCHEMA-SET-002']) - then: 'the schema sets have been deleted' - !schemaSetRepository.findByDataspaceAndName(dataspaceEntity, 'SCHEMA-SET-001').isPresent() - !schemaSetRepository.findByDataspaceAndName(dataspaceEntity, 'SCHEMA-SET-002').isPresent() - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Identifying new module references where #scenario'() { - when: 'identifyNewModuleReferences is called' - def result = objectUnderTest.identifyNewModuleReferences(moduleReferences) - then: 'the correct module references are returned' - assert result.size() == expectedResult.size() - assert result.containsAll(expectedResult) - where: 'the following data is used' - scenario | moduleReferences || expectedResult - 'new module references exist' | toModuleReference([['some module 1' : 'some revision 1'], ['some module 2' : 'some revision 2']]) || toModuleReference([['some module 1' : 'some revision 1'], ['some module 2' : 'some revision 2']]) - 'no new module references exist' | [] || [] - 'module references collection is null'| null || [] - } - - @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) - 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 - } - - @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 - assert !yangResourceRepository.findById(orphanedResourceId).isPresent() - and: 'any shared (still in use by other schema set) yang resources still persists' - def sharedResourceId = 3003L - assert yangResourceRepository.findById(sharedResourceId).isPresent() - } - - @Sql([CLEAR_DATA, SET_DATA]) - def 'Retrieving all yang resources module definition for the given dataspace and anchor name.'() { - when: 'all yang resources module definitions are retrieved for the given dataspace and anchor name' - def result = objectUnderTest.getYangResourceDefinitions('DATASPACE-001', 'ANCHOR3') - then: 'the correct module definitions (moduleName, revision and yang resource content) are returned' - result.sort() == [new ModuleDefinition('MODULE-NAME-004', 'REVISION-004', 'CONTENT-004')] - } - - def assertSchemaSetWithOneModuleIsPersistedCorrectly(expectedYangResourceName, - expectedYangResourceModuleName, - expectedYangResourceRevision, - expectedYangResourceContent, - expectedYangResourceChecksum) { - - // assert the attached yang resource is persisted - def yangResourceEntities = getYangResourceEntities(NEW_SCHEMA_SET_NAME) - assert yangResourceEntities.size() == 1 - - // assert the attached yang resource content - YangResourceEntity yangResourceEntity = yangResourceEntities.iterator().next() - assert yangResourceEntity.id != null - assert yangResourceEntity.fileName == expectedYangResourceName - assert yangResourceEntity.moduleName == expectedYangResourceModuleName - assert yangResourceEntity.revision == expectedYangResourceRevision - assert yangResourceEntity.content == expectedYangResourceContent - assert yangResourceEntity.checksum == expectedYangResourceChecksum - return true - } - - def getYangResourceEntities(schemaSetName) { - def schemaSetEntity = schemaSetRepository - .findByDataspaceAndName(dataspaceEntity, schemaSetName).orElseThrow() - return schemaSetEntity.getYangResources() - } - - def toModuleReference(moduleReferenceAsMap) { - def moduleReferences = [].withDefault { [:] } - moduleReferenceAsMap.forEach(property -> - property.forEach((moduleName, revision) -> { - moduleReferences.add(new ModuleReference(moduleName, revision)) - })) - return moduleReferences - } - -} 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 d6c01f7a9b..7d9c472e69 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 @@ -59,9 +59,9 @@ public class CpsModuleServiceImpl implements CpsModuleService { public void createSchemaSet(final String dataspaceName, final String schemaSetName, final Map<String, String> yangResourcesNameToContentMap) { cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); + cpsModulePersistenceService.storeSchemaSet(dataspaceName, schemaSetName, yangResourcesNameToContentMap); final YangTextSchemaSourceSet yangTextSchemaSourceSet = timedYangTextSchemaSourceSetBuilder.getYangTextSchemaSourceSet(yangResourcesNameToContentMap); - cpsModulePersistenceService.storeSchemaSet(dataspaceName, schemaSetName, yangResourcesNameToContentMap); yangTextSchemaSourceSetCache.updateCache(dataspaceName, schemaSetName, yangTextSchemaSourceSet); } 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 40d4002b97..beb3d4fa8c 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 @@ -87,15 +87,6 @@ public interface CpsModulePersistenceService { Map<String, String> getYangSchemaResources(String dataspaceName, String schemaSetName); /** - * Returns YANG resources per specific dataspace / anchorName. - * - * @param dataspaceName dataspace name - * @param anchorName anchor name - * @return YANG resources (files) map where key is a name and value is content - */ - Map<String, String> getYangSchemaSetResources(String dataspaceName, String anchorName); - - /** * Returns YANG resources module references for the given dataspace name. * * @param dataspaceName dataspace name diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy new file mode 100644 index 0000000000..cfc8ab7ad5 --- /dev/null +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy @@ -0,0 +1,278 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 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.integration.functional + +import org.onap.cps.api.impl.CpsModuleServiceImpl +import org.onap.cps.integration.base.FunctionalSpecBase +import org.onap.cps.spi.CascadeDeleteAllowed +import org.onap.cps.spi.exceptions.AlreadyDefinedException +import org.onap.cps.spi.exceptions.DataspaceNotFoundException +import org.onap.cps.spi.exceptions.ModelValidationException +import org.onap.cps.spi.exceptions.SchemaSetInUseException +import org.onap.cps.spi.exceptions.SchemaSetNotFoundException +import org.onap.cps.spi.model.ModuleDefinition +import org.onap.cps.spi.model.ModuleReference + +class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase { + + CpsModuleServiceImpl objectUnderTest + + private static def originalNumberOfModuleReferences = 1 + private static def existingModuleReference = new ModuleReference('stores','2020-09-15') + static def NEW_RESOURCE_REVISION = '2023-05-10' + static def NEW_RESOURCE_CONTENT = 'module test_module {\n' + + ' yang-version 1.1;\n' + + ' namespace "org:onap:ccsdk:sample";\n' + + '\n' + + ' prefix book-store;\n' + + '\n' + + ' revision "2023-05-10" {\n' + + ' description\n' + + ' "Sample Model";\n' + + ' }' + + '}' + + def newYangResourcesNameToContentMap = [:] + def moduleReferences = [] + + def setup() { + objectUnderTest = cpsModuleService + } + + /* + C R E A T E S C H E M A S E T U S E - C A S E S + */ + + def 'Create new schema set from yang resources with #scenario'() { + given: 'a new schema set with #numberOfModules modules' + populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfNewModules) + when: 'the new schema set is created' + objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', newYangResourcesNameToContentMap) + then: 'the number of module references has increased by #expectedIncrease' + def yangResourceModuleReferences = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1) + originalNumberOfModuleReferences + numberOfNewModules == yangResourceModuleReferences.size() + cleanup: + objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ 'newSchemaSet' ]) + where: 'the following parameters are use' + scenario | numberOfNewModules + 'two valid new modules' | 2 + 'empty schema set' | 0 + 'over max batch size #modules' | 101 + } + + def 'Create new schema set with recommended filename format but invalid yang'() { + given: 'a filename using RFC6020 recommended format (for coverage only)' + def fileName = 'test@2023-05-11.yang' + when: 'attempt to create a schema set with invalid Yang' + objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', [(fileName) :'invalid yang']) + then: 'a model validation exception' + thrown(ModelValidationException) + } + + def 'Create new schema set from modules with #scenario'() { + given: 'a new schema set with #numberOfNewModules modules' + populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfNewModules) + and: 'add existing module references (optional)' + moduleReferences.addAll(existingModuleReferences) + when: 'the new schema set is created' + def schemaSetName = "NewSchemaWith${numberOfNewModules}Modules" + objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences) + and: 'associated with a new anchor' + cpsAdminService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, 'newAnchor') + then: 'the new anchor has the correct number of modules' + def yangResourceModuleReferences = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'newAnchor') + assert expectedNumberOfModulesForAnchor == yangResourceModuleReferences.size() + cleanup: + objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ schemaSetName.toString() ]) + where: 'the following module references are provided' + scenario | numberOfNewModules | existingModuleReferences || expectedNumberOfModulesForAnchor + 'empty schema set' | 0 | [ ] || 0 + 'one existing module' | 0 | [ existingModuleReference ] || 1 + 'two new modules' | 2 | [ ] || 2 + 'two new modules, one existing' | 2 | [ existingModuleReference ] || 3 + 'over max batch size #modules' | 101 | [ ] || 101 + 'two valid, one invalid module' | 2 | [ new ModuleReference('NOT EXIST','IRRELEVANT') ] || 2 + } + + def 'Duplicate schema content.'() { + given: 'a map of yang resources' + populateNewYangResourcesNameToContentMapAndAllModuleReferences(1) + when: 'a new schema set is created' + objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema1', newYangResourcesNameToContentMap) + then: 'the dataspace has one new module (reference)' + def numberOfModuleReferencesAfterFirstSchemaSetHasBeenAdded = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).size() + assert numberOfModuleReferencesAfterFirstSchemaSetHasBeenAdded == originalNumberOfModuleReferences + 1 + when: 'a second new schema set is created' + objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema2', newYangResourcesNameToContentMap) + then: 'the dataspace has no additional module (reference)' + assert numberOfModuleReferencesAfterFirstSchemaSetHasBeenAdded == objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).size() + cleanup: + objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ 'newSchema1', 'newSchema2']) + } + + def 'Create schema set error scenario: #scenario.'() { + when: 'attempt to store schema set #schemaSetName in dataspace #dataspaceName' + populateNewYangResourcesNameToContentMapAndAllModuleReferences(0) + objectUnderTest.createSchemaSet(dataspaceName, schemaSetName, newYangResourcesNameToContentMap) + 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 already exists' | FUNCTIONAL_TEST_DATASPACE_1 | BOOKSTORE_SCHEMA_SET || AlreadyDefinedException + } + + /* + R E A D S C H E M A S E T I N F O U S E - C A S E S + */ + + def 'Retrieving module definitions by anchor.'() { + when: 'the module definitions for an anchor are retrieved' + def result = objectUnderTest.getModuleDefinitionsByAnchorName(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1) + then: 'the correct module definitions are returned' + result == [new ModuleDefinition('stores','2020-09-15','')] + } + + def 'Retrieving yang resource module references by anchor.'() { + when: 'the yang resource module references for an anchor are retrieved' + def result = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1) + then: 'the correct module references are returned' + result == [ existingModuleReference ] + } + + def 'Identifying new module references with #scenario'() { + when: 'identifyNewModuleReferences is called' + def result = objectUnderTest.identifyNewModuleReferences(moduleReferences) + then: 'the correct module references are returned' + assert result.size() == expectedResult.size() + assert result.containsAll(expectedResult) + where: 'the following data is used' + scenario | moduleReferences || expectedResult + 'just new module references' | [new ModuleReference('new1', 'r1'), new ModuleReference('new2', 'r1')] || [new ModuleReference('new1', 'r1'), new ModuleReference('new2', 'r1')] + 'one new module,one existing reference' | [new ModuleReference('new1', 'r1'), existingModuleReference] || [new ModuleReference('new1', 'r1')] + 'no new module references' | [existingModuleReference] || [] + 'no module references' | [] || [] + 'module references collection is null' | null || [] + } + + def 'Retrieve schema set.'() { + when: 'a specific schema set is retreived' + def result = objectUnderTest.getSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET) + then: 'the result has the correct name and module(s)' + assert result.name == 'bookstoreSchemaSet' + assert result.moduleReferences == [ new ModuleReference('stores', '2020-09-15', 'org:onap:ccsdk:sample') ] + } + + def 'Retrieve all schema sets.'() { + given: 'an extra schema set is stored' + populateNewYangResourcesNameToContentMapAndAllModuleReferences(1) + objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema1', newYangResourcesNameToContentMap) + when: 'all schema sets are retrieved' + def result = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1) + then: 'the result contains all expected schema sets' + assert result.name == [ 'bookstoreSchemaSet', 'newSchema1' ] + cleanup: + objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchema1']) + } + + /* + D E L E T E S C H E M A S E T U S E - C A S E S + */ + + def 'Delete schema sets with(out) cascade.'() { + given: 'a schema set' + populateNewYangResourcesNameToContentMapAndAllModuleReferences(1) + objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', newYangResourcesNameToContentMap) + and: 'optionally create anchor for the schema set' + if (associateWithAnchor) { + cpsAdminService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', 'newAnchor') + } + when: 'attempt to delete the schema set' + try { + objectUnderTest.deleteSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', cascadeDeleteAllowedOption) + } + catch (Exception e) { // only accept correct exception when schema set cannot be deleted + assert e instanceof SchemaSetInUseException && expectSchemaSetStillPresent + } + then: 'check if the dataspace still contains the new schema set or not' + def remainingSchemaSetNames = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1).name + assert remainingSchemaSetNames.contains('newSchemaSet') == expectSchemaSetStillPresent + cleanup: + objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchemaSet']) + where: 'the following options are used' + associateWithAnchor | cascadeDeleteAllowedOption || expectSchemaSetStillPresent + false | CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED || false + false | CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED || false + true | CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED || false + true | CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED || true + } + + def 'Delete schema sets with shared resources.'() { + given: 'a new schema set' + populateNewYangResourcesNameToContentMapAndAllModuleReferences(1) + objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet1', newYangResourcesNameToContentMap) + and: 'another schema set which shares one yang resource (module)' + populateNewYangResourcesNameToContentMapAndAllModuleReferences(2) + objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet2', newYangResourcesNameToContentMap) + when: 'all schema sets are retrieved' + def moduleRevisions = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).revision + then: 'both modules (revisions) are present' + assert moduleRevisions.containsAll(['2000-01-01', '2000-01-01']) + when: 'delete the second schema set that has two resources one of which is a shared resource' + objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchemaSet2']) + then: 'only the second schema set is deleted' + def remainingSchemaSetNames = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1).name + assert remainingSchemaSetNames.contains('newSchemaSet1') + assert !remainingSchemaSetNames.contains('newSchemaSet2') + and: 'only the shared module (revision) remains' + def remainingModuleRevisions = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).revision + assert remainingModuleRevisions.contains('2000-01-01') + assert !remainingModuleRevisions.contains('2001-01-01') + cleanup: + objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchemaSet1']) + } + + def 'Delete schema set error scenario: #scenario.'() { + when: 'attempt to delete a schema set where #scenario' + objectUnderTest.deleteSchemaSet(dataspaceName, schemaSetName, CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED) + 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' | FUNCTIONAL_TEST_DATASPACE_1 | 'unknown' || SchemaSetNotFoundException + } + + /* + H E L P E R M E T H O D S + */ + + def populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfModules) { + numberOfModules.times { + def uniqueName = 'name_' + it + def uniqueRevision = String.valueOf(2000 + it) + '-01-01' + moduleReferences.add(new ModuleReference(uniqueName, uniqueRevision)) + def uniqueContent = NEW_RESOURCE_CONTENT.replace(NEW_RESOURCE_REVISION, uniqueRevision) + newYangResourcesNameToContentMap.put(uniqueRevision, uniqueContent) + } + } + +} |