diff options
42 files changed, 556 insertions, 663 deletions
diff --git a/checkstyle/src/main/CopyrightCheck.py b/checkstyle/src/main/CopyrightCheck.py index 8f1dbff571..ff3d4029b5 100644 --- a/checkstyle/src/main/CopyrightCheck.py +++ b/checkstyle/src/main/CopyrightCheck.py @@ -221,7 +221,7 @@ def CheckCopyrightFormat(copyrightInFile, templateCopyright, filePath): for copyrightInFileKey, templateLine in zip(copyrightInFile, templateCopyright): if copyrightInFile[copyrightInFileKey] != templateLine: issueCounter += 1 - errorWithComparison += filePath + ' | line ' + '{:2}'.format(copyrightInFileKey) + ' read \t ' + repr(copyrightInFile[copyrightInFileKey]) + '\n' + errorWithComparison += filePath + ' | line ' + '{:2}'.format(copyrightInFileKey) + ' read \t ' + repr(copyrightInFile[copyrightInFileKey]) + '\n' errorWithComparison += filePath + ' | line ' + '{:2}'.format(copyrightInFileKey) + ' expected ' + repr(templateLine) + '\n' if errorWithComparison != '': print(errorWithComparison.rstrip('\n')) diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java index 35bd578ce2..7db754279c 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java @@ -55,10 +55,9 @@ public class NcmpDatastoreResourceRequestHandlerFactory { return new NcmpDatastorePassthroughRunningResourceRequestHandler(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled); case PASSTHROUGH_OPERATIONAL: + default: return new NcmpDatastorePassthroughOperationalResourceRequestHandler(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled); - default: - return null; } } } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java index 097dd0af49..64a9934b99 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java @@ -62,9 +62,7 @@ public interface CmHandleStateMapper { dataStores.setOperational(operationalSyncState); } - return dataStores; - } /** diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperTest.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy index 663b9d02a6..9a09b97973 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperTest.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy @@ -32,7 +32,7 @@ import java.time.OffsetDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter -class CmHandleStateMapperTest extends Specification { +class CmHandleStateMapperSpec extends Specification { def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ") .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC)) @@ -59,6 +59,11 @@ class CmHandleStateMapperTest extends Specification { assert result.dataSyncState.operational.getSyncState() != null } + def 'Handling null state.'() { + expect: 'converting null returns null' + objectUnderTest.toDataStores(null) == null + } + def 'Internal to External Lock Reason Mapping of #scenario'() { given: 'a LOCKED composite state with locked reason of #scenario' def compositeState = new CompositeStateBuilder() diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index 0983889802..27c3646a5e 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -67,7 +67,6 @@ import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.CmHandleQueryServiceParameters; import org.onap.cps.spi.model.ModuleDefinition; import org.onap.cps.spi.model.ModuleReference; -import org.onap.cps.utils.CpsValidator; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @@ -148,20 +147,17 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService final OperationEnum operation, final String requestData, final String dataType) { - CpsValidator.validateNameCharacters(cmHandleId); return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, operation, requestData, dataType); } @Override public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleId) { - CpsValidator.validateNameCharacters(cmHandleId); return inventoryPersistence.getYangResourcesModuleReferences(cmHandleId); } @Override public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(final String cmHandleId) { - CpsValidator.validateNameCharacters(cmHandleId); return inventoryPersistence.getModuleDefinitionsByCmHandleId(cmHandleId); } @@ -206,7 +202,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService */ @Override public void setDataSyncEnabled(final String cmHandleId, final boolean dataSyncEnabled) { - CpsValidator.validateNameCharacters(cmHandleId); final CompositeState compositeState = inventoryPersistence .getCmHandleState(cmHandleId); if (compositeState.getDataSyncEnabled().equals(dataSyncEnabled)) { @@ -250,7 +245,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService */ @Override public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleId) { - CpsValidator.validateNameCharacters(cmHandleId); return YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle( inventoryPersistence.getYangModelCmHandle(cmHandleId)); } @@ -263,7 +257,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService */ @Override public Map<String, String> getCmHandlePublicProperties(final String cmHandleId) { - CpsValidator.validateNameCharacters(cmHandleId); final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); final List<YangModelCmHandle.Property> yangModelPublicProperties = yangModelCmHandle.getPublicProperties(); @@ -280,7 +273,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService */ @Override public CompositeState getCmHandleCompositeState(final String cmHandleId) { - CpsValidator.validateNameCharacters(cmHandleId); return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState(); } @@ -320,7 +312,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService new ArrayList<>(tobeRemovedCmHandles.size()); for (final String cmHandleId : tobeRemovedCmHandles) { try { - CpsValidator.validateNameCharacters(cmHandleId); final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); lcmEventsCmHandleStateHandler.updateCmHandleState(yangModelCmHandle, CmHandleState.DELETING); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java index d9aeaf2589..f39c2f9b60 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java @@ -43,7 +43,6 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DataNodeBuilder; -import org.onap.cps.utils.CpsValidator; import org.springframework.stereotype.Service; @Slf4j @@ -67,7 +66,6 @@ public class NetworkCmProxyDataServicePropertyHandler { for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) { final String cmHandleId = ncmpServiceCmHandle.getCmHandleId(); try { - CpsValidator.validateNameCharacters(cmHandleId); final DataNode existingCmHandleDataNode = inventoryPersistence.getCmHandleDataNode(cmHandleId); processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle); cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java index d46d634104..83faa005f0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -16,7 +16,7 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= + * ============LICENSE_END========================================================= */ package org.onap.cps.ncmp.api.impl.operations; @@ -33,7 +33,6 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.inventory.CmHandleState; import org.onap.cps.ncmp.api.inventory.InventoryPersistence; import org.onap.cps.spi.exceptions.CpsException; -import org.onap.cps.utils.CpsValidator; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -131,7 +130,6 @@ public class DmiDataOperations extends DmiOperations { } private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { - CpsValidator.validateNameCharacters(cmHandleId); return inventoryPersistence.getYangModelCmHandle(cmHandleId); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java index b679107251..5f4a6540c2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java @@ -15,7 +15,7 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= + * ============LICENSE_END========================================================= */ package org.onap.cps.ncmp.api.impl.utils; @@ -30,7 +30,7 @@ import org.apache.logging.log4j.util.TriConsumer; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; import org.onap.cps.ncmp.api.impl.operations.DmiOperations; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.utils.CpsValidator; +import org.onap.cps.spi.utils.CpsValidator; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -41,6 +41,7 @@ import org.springframework.web.util.UriComponentsBuilder; public class DmiServiceUrlBuilder { private final NcmpConfiguration.DmiProperties dmiProperties; + private final CpsValidator cpsValidator; /** * This method creates the dmi service url. @@ -84,7 +85,7 @@ public class DmiServiceUrlBuilder { public Map<String, Object> populateUriVariables(final YangModelCmHandle yangModelCmHandle, final String cmHandleId, final DmiOperations.DataStoreEnum dataStore) { - CpsValidator.validateNameCharacters(cmHandleId); + cpsValidator.validateNameCharacters(cmHandleId); final Map<String, Object> uriVariables = new HashMap<>(); final String dmiBasePath = dmiProperties.getDmiBasePath(); uriVariables.put("dmiServiceName", diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java index f842ddbc1a..aabc52c592 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java @@ -37,7 +37,6 @@ import lombok.Setter; import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService; import org.onap.cps.ncmp.api.inventory.CompositeState; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -import org.onap.cps.utils.CpsValidator; /** * Cm Handle which follows the Yang resource dmi registry model when persisting data to DMI or the DB. @@ -104,7 +103,6 @@ public class YangModelCmHandle { final String dmiDataServiceName, final String dmiModelServiceName, final NcmpServiceCmHandle ncmpServiceCmHandle) { - CpsValidator.validateNameCharacters(ncmpServiceCmHandle.getCmHandleId()); final YangModelCmHandle yangModelCmHandle = new YangModelCmHandle(); yangModelCmHandle.setId(ncmpServiceCmHandle.getCmHandleId()); yangModelCmHandle.setDmiServiceName(dmiServiceName); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImpl.java index eed47eddab..adba198408 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImpl.java @@ -46,7 +46,7 @@ import org.onap.cps.spi.model.Anchor; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleDefinition; import org.onap.cps.spi.model.ModuleReference; -import org.onap.cps.utils.CpsValidator; +import org.onap.cps.spi.utils.CpsValidator; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Component; @@ -73,11 +73,14 @@ public class InventoryPersistenceImpl implements InventoryPersistence { private final CpsAdminPersistenceService cpsAdminPersistenceService; + private final CpsValidator cpsValidator; + @Override public CompositeState getCmHandleState(final String cmHandleId) { final DataNode stateAsDataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId) + "/state", - FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); + String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId) + "/state", + FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); + cpsValidator.validateNameCharacters(cmHandleId); return new CompositeStateBuilder().fromDataNode(stateAsDataNode).build(); } @@ -102,7 +105,7 @@ public class InventoryPersistenceImpl implements InventoryPersistence { @Override public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { - CpsValidator.validateNameCharacters(cmHandleId); + cpsValidator.validateNameCharacters(cmHandleId); return YangDataConverter.convertCmHandleToYangModel(getCmHandleDataNode(cmHandleId), cmHandleId); } @@ -113,7 +116,7 @@ public class InventoryPersistenceImpl implements InventoryPersistence { @Override public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleId) { - CpsValidator.validateNameCharacters(cmHandleId); + cpsValidator.validateNameCharacters(cmHandleId); return cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId); } @@ -143,7 +146,7 @@ public class InventoryPersistenceImpl implements InventoryPersistence { @Override public void deleteSchemaSetWithCascade(final String schemaSetName) { try { - CpsValidator.validateNameCharacters(schemaSetName); + cpsValidator.validateNameCharacters(schemaSetName); cpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName, CASCADE_DELETE_ALLOWED); } catch (final SchemaSetNotFoundException schemaSetNotFoundException) { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy index 3a0f3c73b2..e6c79f89a1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy @@ -1,5 +1,5 @@ /* - * ============LICENSE_START======================================================= + * ============LICENSE_START======================================================= * Copyright (C) 2021-2022 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ @@ -222,7 +222,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { where: scenario | cmHandleId | exception || expectedError | expectedErrorText 'cm-handle already exist' | 'cmhandle' | new AlreadyDefinedExceptionBatch(["path[@id='${cmHandleId}']".toString()]) || CM_HANDLE_ALREADY_EXIST | 'cm-handle already exists' - 'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException("", "") || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id' 'unknown exception while registering cm-handle' | 'cmhandle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 91eb218eaf..58ca06bee4 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -33,10 +33,12 @@ import org.onap.cps.ncmp.api.inventory.InventoryPersistence import org.onap.cps.ncmp.api.inventory.LockReasonCategory import org.onap.cps.ncmp.api.inventory.DataStoreSyncState import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters +import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse import org.onap.cps.ncmp.api.models.ConditionApiProperties import org.onap.cps.ncmp.api.models.DmiPluginRegistration import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.spi.exceptions.CpsException +import org.onap.cps.spi.exceptions.DataNodeNotFoundException import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.CmHandleQueryServiceParameters import spock.lang.Shared @@ -55,6 +57,10 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum. import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE +import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST +import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_INVALID_ID +import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR + class NetworkCmProxyDataServiceImplSpec extends Specification { @@ -105,17 +111,6 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { >> { new ResponseEntity<>(HttpStatus.CREATED) } } - def 'Write resource data for pass-through running from DMI using an invalid id.'() { - when: 'write resource data is called' - objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('invalid cm handle name', - 'testResourceId', CREATE, - '{some-json}', 'application/json') - then: 'exception is thrown' - thrown(DataValidationException.class) - and: 'DMI is not invoked' - 0 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi(_, _, _, _, _) - } - def 'Get resource data for pass-through operational from DMI.'() { given: 'get data node is called' mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', @@ -166,15 +161,6 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { 1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle') } - def 'Getting Yang Resources with an invalid #scenario.'() { - when: 'yang resources is called' - objectUnderTest.getYangResourcesModuleReferences('invalid cm handle with spaces') - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'CPS module services is not invoked' - 0 * mockInventoryPersistence.getYangResourcesModuleReferences(*_) - } - def 'Get a cm handle.'() { given: 'the system returns a yang modelled cm handle' def dmiServiceName = 'some service name' @@ -203,15 +189,6 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { } - def 'Get a cm handle with an invalid id.'() { - when: 'getting cm handle details for a given cm handle id with an invalid name' - objectUnderTest.getNcmpServiceCmHandle('invalid cm handle with spaces') - then: 'an exception is thrown' - thrown(DataValidationException) - and: 'the yang model cm handle retriever is not invoked' - 0 * mockInventoryPersistence.getYangModelCmHandle(*_) - } - def 'Get cm handle public properties'() { given: 'a yang modelled cm handle' def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')] @@ -225,15 +202,6 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { result == [ 'public prop' : 'some public prop' ] } - def 'Get cm handle public properties with an invalid id.'() { - when: 'getting cm handle public properties for a given cm handle id with an invalid name' - objectUnderTest.getCmHandlePublicProperties('invalid cm handle with spaces') - then: 'an exception is thrown' - thrown(DataValidationException) - and: 'the yang model cm handle retriever is not invoked' - 0 * mockInventoryPersistence.getYangModelCmHandle(*_) - } - def 'Get cm handle composite state'() { given: 'a yang modelled cm handle' def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, @@ -252,15 +220,6 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { result == compositeState } - def 'Get cm handle composite state with an invalid id.'() { - when: 'getting cm handle composite state for a given cm handle id with an invalid name' - objectUnderTest.getCmHandleCompositeState('invalid cm handle with spaces') - then: 'an exception is thrown' - thrown(DataValidationException) - and: 'the yang model cm handle retriever is not invoked' - 0 * mockInventoryPersistence.getYangModelCmHandle(_) - } - def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() { given: 'cpsDataService returns valid datanode' mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy index 3a82ee3400..c4d0020a6c 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy @@ -15,7 +15,7 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= + * ============LICENSE_END========================================================= */ package org.onap.cps.ncmp.api.impl.operations @@ -28,6 +28,7 @@ import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder import org.onap.cps.ncmp.api.inventory.CmHandleState import org.onap.cps.ncmp.api.inventory.CompositeState import org.onap.cps.ncmp.api.inventory.InventoryPersistence +import org.onap.cps.spi.utils.CpsValidator import org.spockframework.spring.SpringBean import spock.lang.Shared import spock.lang.Specification @@ -43,11 +44,13 @@ abstract class DmiOperationsBaseSpec extends Specification { @SpringBean InventoryPersistence mockInventoryPersistence = Mock() + def mockCpsValidator = Mock(CpsValidator) + @SpringBean ObjectMapper spyObjectMapper = Spy() @SpringBean - DmiServiceUrlBuilder dmiServiceUrlBuilder = new DmiServiceUrlBuilder(new NcmpConfiguration.DmiProperties()) + DmiServiceUrlBuilder dmiServiceUrlBuilder = new DmiServiceUrlBuilder(new NcmpConfiguration.DmiProperties(), mockCpsValidator) def yangModelCmHandle = new YangModelCmHandle() def static dmiServiceName = 'some service name' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImplSpec.groovy index 0d459fd0fa..c713aad3c4 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImplSpec.groovy @@ -29,11 +29,11 @@ import org.onap.cps.spi.CascadeDeleteAllowed import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.CpsAdminPersistenceService import org.onap.cps.spi.FetchDescendantsOption -import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.ModuleDefinition import org.onap.cps.spi.model.ModuleReference import org.onap.cps.utils.JsonObjectMapper +import org.onap.cps.spi.utils.CpsValidator import spock.lang.Shared import spock.lang.Specification @@ -56,8 +56,10 @@ class InventoryPersistenceImplSpec extends Specification { def mockCpsAdminPersistenceService = Mock(CpsAdminPersistenceService) + def mockCpsValidator = Mock(CpsValidator) + def objectUnderTest = new InventoryPersistenceImpl(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService, - mockCpsDataPersistenceService, mockCpsAdminPersistenceService) + mockCpsDataPersistenceService, mockCpsAdminPersistenceService, mockCpsValidator) def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ") .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC)) @@ -95,34 +97,29 @@ class InventoryPersistenceImplSpec extends Specification { result.publicProperties == expectedPublicProperties and: 'the state details are returned' result.compositeState.cmHandleState == expectedCompositeState + and: 'the CM Handle ID is validated' + 1 * mockCpsValidator.validateNameCharacters(cmHandleId) where: 'the following parameters are used' scenario | childDataNodes || expectedDmiProperties || expectedPublicProperties || expectedCompositeState 'no properties' | [] || [] || [] || null - 'DMI and public properties' | childDataNodesForCmHandleWithAllProperties || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")] || null + 'DMI and public properties' | childDataNodesForCmHandleWithAllProperties || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")] || null 'just DMI properties' | childDataNodesForCmHandleWithDMIProperties || [new YangModelCmHandle.Property("name1", "value1")] || [] || null 'just public properties' | childDataNodesForCmHandleWithPublicProperties || [] || [new YangModelCmHandle.Property("name2", "value2")] || null 'with state details' | childDataNodesForCmHandleWithState || [] || [] || CmHandleState.ADVISED } - def "Retrieve CmHandle using datanode with invalid CmHandle id."() { - when: 'retrieving the yang modelled cm handle with an invalid id' - def result = objectUnderTest.getYangModelCmHandle('cm handle id with spaces') - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the result is not returned' - result == null - } - - def "Handling missing service names as null CPS-1043."() { + def "Handling missing service names as null."() { given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes' def dataNode = new DataNode(childDataNodes:[], leaves: [:]) mockCpsDataPersistenceService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode when: 'retrieving the yang modelled cm handle' def result = objectUnderTest.getYangModelCmHandle(cmHandleId) - then: 'the service names ae returned as null' + then: 'the service names are returned as null' result.dmiServiceName == null result.dmiDataServiceName == null result.dmiModelServiceName == null + and: 'the CM Handle ID is validated' + 1 * mockCpsValidator.validateNameCharacters(cmHandleId) } def 'Get a Cm Handle Composite State'() { @@ -136,6 +133,8 @@ class InventoryPersistenceImplSpec extends Specification { def result = objectUnderTest.getCmHandleState(cmHandleId) then: 'result has returned the correct cm handle state' result.cmHandleState == CmHandleState.ADVISED + and: 'the CM Handle ID is validated' + 1 * mockCpsValidator.validateNameCharacters(cmHandleId) } def 'Update Cm Handle with #scenario State'() { @@ -187,6 +186,8 @@ class InventoryPersistenceImplSpec extends Specification { def result = objectUnderTest.getYangResourcesModuleReferences('some-cmHandle-Id') then: 'the returned result is a collection of module definitions' assert result == moduleReferences + and: 'the CM Handle ID is validated' + 1 * mockCpsValidator.validateNameCharacters('some-cmHandle-Id') } def 'Save Cmhandle'() { @@ -230,15 +231,8 @@ class InventoryPersistenceImplSpec extends Specification { objectUnderTest.deleteSchemaSetWithCascade('validSchemaSetName') then: 'the module service to delete schemaSet is invoked once' 1 * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'validSchemaSetName', CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED) - } - - def 'Delete schema set with an invalid schema set name'() { - when: 'the method to delete schema set is called with an invalid schema set name' - objectUnderTest.deleteSchemaSetWithCascade('invalid SchemaSet name') - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the module service to delete schemaSet is not called' - 0 * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'sampleSchemaSetName', CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED) + and: 'the CM Handle ID is validated' + 1 * mockCpsValidator.validateNameCharacters('validSchemaSetName') } def 'Get data node via xPath'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy index 2d993973eb..09f4550319 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy @@ -15,11 +15,12 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= + * ============LICENSE_END========================================================= */ package org.onap.cps.ncmp.api.utils +import org.onap.cps.spi.utils.CpsValidator import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING @@ -38,7 +39,9 @@ class DmiServiceUrlBuilderSpec extends Specification { NcmpConfiguration.DmiProperties dmiProperties = new NcmpConfiguration.DmiProperties() - def objectUnderTest = new DmiServiceUrlBuilder(dmiProperties) + def mockCpsValidator = Mock(CpsValidator) + + def objectUnderTest = new DmiServiceUrlBuilder(dmiProperties, mockCpsValidator) def 'Create the dmi service url with #scenario.'() { given: 'uri variables' diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml index 824a8d9710..a193baf9fb 100644 --- a/cps-ri/pom.xml +++ b/cps-ri/pom.xml @@ -88,6 +88,10 @@ <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.hazelcast</groupId>
+ <artifactId>hazelcast-spring</artifactId>
+ </dependency>
<!-- T E S T D E P E N D E N C I E S -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/cache/AnchorDataCacheConfig.java b/cps-ri/src/main/java/org/onap/cps/spi/cache/AnchorDataCacheConfig.java new file mode 100644 index 0000000000..f956120e4a --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/cache/AnchorDataCacheConfig.java @@ -0,0 +1,72 @@ +/* + * ============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.spi.cache; + +import com.hazelcast.config.Config; +import com.hazelcast.config.MapConfig; +import com.hazelcast.config.NamedConfig; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.map.IMap; +import java.util.concurrent.TimeUnit; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Core infrastructure of the hazelcast distributed cache for anchor data config use cases. + */ +@Configuration +public class AnchorDataCacheConfig { + + public static final long ANCHOR_DATA_CACHE_TTL_SECS = TimeUnit.HOURS.toSeconds(1); + private static final MapConfig anchorDataCacheMapConfig = createMapConfig("anchorDataCacheMapConfig"); + + /** + * Distributed instance of anchor data cache that contains module prefix by anchor name as properties. + * + * @return configured map of anchor data cache + */ + @Bean + public IMap<String, AnchorDataCacheEntry> anchorDataCache() { + return createHazelcastInstance("hazelCastInstanceCpsRi", anchorDataCacheMapConfig) + .getMap("anchorDataCache"); + } + + private HazelcastInstance createHazelcastInstance(final String hazelcastInstanceName, + final NamedConfig namedConfig) { + return Hazelcast.newHazelcastInstance(initializeConfig(hazelcastInstanceName, namedConfig)); + } + + private Config initializeConfig(final String instanceName, final NamedConfig namedConfig) { + final Config config = new Config(instanceName); + config.addMapConfig((MapConfig) namedConfig); + config.setClusterName("cps-ri-caches"); + return config; + } + + private static MapConfig createMapConfig(final String configName) { + final MapConfig mapConfig = new MapConfig(configName); + mapConfig.setBackupCount(3); + mapConfig.setAsyncBackupCount(3); + return mapConfig; + } + +} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/cache/AnchorDataCacheEntry.java b/cps-ri/src/main/java/org/onap/cps/spi/cache/AnchorDataCacheEntry.java new file mode 100644 index 0000000000..98f6ec3f13 --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/cache/AnchorDataCacheEntry.java @@ -0,0 +1,44 @@ +/* + * ============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.spi.cache; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +public class AnchorDataCacheEntry implements Serializable { + + private static final long serialVersionUID = 2111243947810370698L; + + private Map<String, Object> properties = new HashMap<>(); + + public Object getProperty(final String propertyName) { + return properties.get(propertyName); + } + + public void setProperty(final String propertyName, final Object value) { + properties.put(propertyName, value); + } + + public boolean hasProperty(final String propertyName) { + return properties.containsKey(propertyName); + } +} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index ebc851a443..06c12a89d2 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -24,6 +24,7 @@ package org.onap.cps.spi.impl; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; +import com.hazelcast.map.IMap; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -32,6 +33,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -44,6 +46,8 @@ import org.onap.cps.cpspath.parser.CpsPathUtil; import org.onap.cps.cpspath.parser.PathParsingException; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.cache.AnchorDataCacheConfig; +import org.onap.cps.spi.cache.AnchorDataCacheEntry; import org.onap.cps.spi.entities.AnchorEntity; import org.onap.cps.spi.entities.DataspaceEntity; import org.onap.cps.spi.entities.FragmentEntity; @@ -73,18 +77,16 @@ import org.springframework.stereotype.Service; public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService { private final DataspaceRepository dataspaceRepository; - private final AnchorRepository anchorRepository; - private final FragmentRepository fragmentRepository; - private final JsonObjectMapper jsonObjectMapper; - private final SessionManager sessionManager; + private final IMap<String, AnchorDataCacheEntry> anchorDataCache; private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})"; private static final Pattern REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE = Pattern.compile("\\[(\\@([^\\/]{0,9999}))\\]$"); + private static final String TOP_LEVEL_MODULE_PREFIX_PROPERTY_NAME = "topLevelModulePrefix"; @Override public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath, @@ -218,7 +220,9 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath, final FetchDescendantsOption fetchDescendantsOption) { final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath); - return toDataNode(fragmentEntity, fetchDescendantsOption); + final DataNode dataNode = toDataNode(fragmentEntity, fetchDescendantsOption); + dataNode.setModuleNamePrefix(getRootModuleNamePrefix(fragmentEntity.getAnchor())); + return dataNode; } private FragmentEntity getFragmentByXpath(final String dataspaceName, final String anchorName, @@ -260,8 +264,11 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService : fragmentRepository.findAllByAnchorAndXpathIn(anchorEntity, ancestorXpaths); } return fragmentEntities.stream() - .map(fragmentEntity -> toDataNode(fragmentEntity, fetchDescendantsOption)) - .collect(Collectors.toUnmodifiableList()); + .map(fragmentEntity -> { + final DataNode dataNode = toDataNode(fragmentEntity, fetchDescendantsOption); + dataNode.setModuleNamePrefix(getRootModuleNamePrefix(anchorEntity)); + return dataNode; + }).collect(Collectors.toUnmodifiableList()); } @Override @@ -303,14 +310,23 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService leaves = jsonObjectMapper.convertJsonString(fragmentEntity.getAttributes(), Map.class); } return new DataNodeBuilder() - .withModuleNamePrefix(getFirstModuleName(fragmentEntity)) .withXpath(fragmentEntity.getXpath()) .withLeaves(leaves) .withChildDataNodes(childDataNodes).build(); } - private String getFirstModuleName(final FragmentEntity fragmentEntity) { - final SchemaSetEntity schemaSetEntity = fragmentEntity.getAnchor().getSchemaSet(); + private String getRootModuleNamePrefix(final AnchorEntity anchorEntity) { + final String cachedModuleNamePrefix = getModuleNamePrefixFromCache(anchorEntity); + if (cachedModuleNamePrefix != null) { + return cachedModuleNamePrefix; + } + final String moduleNamePrefix = buildSchemaContextAndRetrieveModulePrefix(anchorEntity); + cacheModuleNamePrefix(anchorEntity.getName(), moduleNamePrefix); + return moduleNamePrefix; + } + + private String buildSchemaContextAndRetrieveModulePrefix(final AnchorEntity anchorEntity) { + final SchemaSetEntity schemaSetEntity = anchorEntity.getSchemaSet(); final Map<String, String> yangResourceNameToContent = schemaSetEntity.getYangResources().stream().collect( Collectors.toMap(YangResourceEntity::getFileName, YangResourceEntity::getContent)); @@ -319,6 +335,24 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return schemaContext.getModules().iterator().next().getName(); } + private void cacheModuleNamePrefix(final String anchorName, final String moduleNamePrefix) { + final AnchorDataCacheEntry anchorDataCacheEntry = new AnchorDataCacheEntry(); + anchorDataCacheEntry.setProperty(TOP_LEVEL_MODULE_PREFIX_PROPERTY_NAME, moduleNamePrefix); + if (anchorDataCache.putIfAbsent(anchorName, anchorDataCacheEntry, + AnchorDataCacheConfig.ANCHOR_DATA_CACHE_TTL_SECS, TimeUnit.SECONDS) == null) { + log.debug("Module name prefix for an anchor {} is cached", anchorName); + } + } + + private String getModuleNamePrefixFromCache(final AnchorEntity anchorEntity) { + if (anchorDataCache.containsKey(anchorEntity.getName())) { + final AnchorDataCacheEntry anchorDataCacheEntry = anchorDataCache.get(anchorEntity.getName()); + return anchorDataCacheEntry.hasProperty(TOP_LEVEL_MODULE_PREFIX_PROPERTY_NAME) + ? anchorDataCacheEntry.getProperty(TOP_LEVEL_MODULE_PREFIX_PROPERTY_NAME).toString() : null; + } + return null; + } + private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity, final FetchDescendantsOption fetchDescendantsOption) { if (fetchDescendantsOption.hasNext()) { diff --git a/cps-service/src/main/java/org/onap/cps/utils/CpsValidator.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/utils/CpsValidatorImpl.java index f3774d9529..0645831425 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/CpsValidator.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/utils/CpsValidatorImpl.java @@ -18,27 +18,25 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.utils; +package org.onap.cps.spi.impl.utils; import com.google.common.collect.Lists; import java.util.Collection; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.spi.exceptions.DataValidationException; +import org.onap.cps.spi.utils.CpsValidator; +import org.springframework.stereotype.Component; @Slf4j -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class CpsValidator { +@Component +@RequiredArgsConstructor +public class CpsValidatorImpl implements CpsValidator { private static final char[] UNSUPPORTED_NAME_CHARACTERS = "!\" #$%&'()*+,./\\:;<=>?@[]^`{|}~".toCharArray(); - /** - * Validate characters in names within cps. - * - * @param names names of data to be validated - */ - public static void validateNameCharacters(final String... names) { + @Override + public void validateNameCharacters(final String... names) { for (final String name : names) { final Collection<Character> charactersOfName = Lists.charactersOf(name); for (final char unsupportedCharacter : UNSUPPORTED_NAME_CHARACTERS) { @@ -50,7 +48,4 @@ public final class CpsValidator { } } } - - - } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/cache/AnchorDataCacheConfigSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/cache/AnchorDataCacheConfigSpec.groovy new file mode 100644 index 0000000000..a77db1be88 --- /dev/null +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/cache/AnchorDataCacheConfigSpec.groovy @@ -0,0 +1,52 @@ +/* + * ============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.spi.cache +import com.hazelcast.core.Hazelcast +import com.hazelcast.map.IMap +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +@SpringBootTest +@ContextConfiguration(classes = [AnchorDataCacheConfig]) +class AnchorDataCacheConfigSpec extends Specification { + + @Autowired + private IMap<String, AnchorDataCacheEntry> anchorDataCache + + def 'Embedded (hazelcast) cache for Anchor Data.'() { + expect: 'system is able to create an instance of the Anchor data cache' + assert null != anchorDataCache + and: 'there is at least 1 instance' + assert Hazelcast.allHazelcastInstances.size() > 0 + and: 'anchorDataCache is present' + assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceCpsRi') + } + + def 'Verify configs for Distributed Caches'(){ + given: 'the Anchor Data Cache config' + def anchorDataCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelCastInstanceCpsRi').config.mapConfigs.get('anchorDataCacheMapConfig') + expect: 'system created instance with correct config' + assert anchorDataCacheConfig.backupCount == 3 + assert anchorDataCacheConfig.asyncBackupCount == 3 + } +} diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/cache/AnchorDataCacheEntrySpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/cache/AnchorDataCacheEntrySpec.groovy new file mode 100644 index 0000000000..103631ec2f --- /dev/null +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/cache/AnchorDataCacheEntrySpec.groovy @@ -0,0 +1,40 @@ +/* + * ============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.spi.cache + +import org.onap.cps.spi.cache.AnchorDataCacheEntry +import spock.lang.Specification + +class AnchorDataCacheEntrySpec extends Specification { + + def objectUnderTest = new AnchorDataCacheEntry() + + def 'Anchor Data Cache Properties Management.'() { + when: 'a property named "sample" is added to the cache' + objectUnderTest.setProperty('sample', 123) + then: 'the cache has that property' + assert objectUnderTest.hasProperty('sample') + and: 'the value is correct' + assert objectUnderTest.getProperty('sample') == 123 + and: 'the cache does not have an an object called "something else"' + assert objectUnderTest.hasProperty('something else') == false + } +} diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy index 470b03afdf..3b15b7607f 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy @@ -22,6 +22,7 @@ package org.onap.cps.spi.impl import com.fasterxml.jackson.databind.ObjectMapper import org.hibernate.StaleStateException import org.onap.cps.spi.FetchDescendantsOption +import org.onap.cps.spi.cache.AnchorDataCacheEntry import org.onap.cps.spi.entities.AnchorEntity import org.onap.cps.spi.entities.FragmentEntity import org.onap.cps.spi.entities.SchemaSetEntity @@ -37,6 +38,7 @@ import org.onap.cps.spi.utils.SessionManager import org.onap.cps.utils.JsonObjectMapper import spock.lang.Shared import spock.lang.Specification +import com.hazelcast.map.IMap; class CpsDataPersistenceServiceSpec extends Specification { @@ -45,9 +47,10 @@ class CpsDataPersistenceServiceSpec extends Specification { def mockFragmentRepository = Mock(FragmentRepository) def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) def mockSessionManager = Mock(SessionManager) + def mockAnchorDataCache = Mock(IMap<String, AnchorDataCacheEntry>) def objectUnderTest = new CpsDataPersistenceServiceImpl( - mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper,mockSessionManager) + mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper, mockSessionManager, mockAnchorDataCache) @Shared def NEW_RESOURCE_CONTENT = 'module stores {\n' + @@ -211,4 +214,4 @@ class CpsDataPersistenceServiceSpec extends Specification { } return dataNode } -}
\ No newline at end of file +} diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy index d6f10d809d..1fbff654f8 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy @@ -23,7 +23,11 @@ package org.onap.cps.spi.impl import com.fasterxml.jackson.databind.ObjectMapper +import com.hazelcast.config.Config +import com.hazelcast.instance.impl.HazelcastInstanceFactory +import com.hazelcast.map.IMap import org.onap.cps.DatabaseTestContainer +import org.onap.cps.spi.cache.AnchorDataCacheEntry import org.onap.cps.spi.repository.AnchorRepository import org.onap.cps.spi.repository.DataspaceRepository import org.onap.cps.spi.repository.FragmentRepository @@ -58,6 +62,12 @@ class CpsPersistenceSpecBase extends Specification { @SpringBean JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + // Instantiate Hazelcast with different name for testing purposes! + @SpringBean + IMap<String, AnchorDataCacheEntry> anchorDataCache = HazelcastInstanceFactory + .getOrCreateHazelcastInstance(new Config('hazelcastTestInstance')) + .getMap('testAnchorDataCacheMap') + static final String CLEAR_DATA = '/data/clear-all.sql' static final String DATASPACE_NAME = 'DATASPACE-001' diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/CpsValidatorSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/utils/CpsValidatorSpec.groovy index ea7a5d6d1d..ae2ff16bc1 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/CpsValidatorSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/utils/CpsValidatorSpec.groovy @@ -18,23 +18,25 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.utils +package org.onap.cps.spi.impl.utils import org.onap.cps.spi.exceptions.DataValidationException import spock.lang.Specification class CpsValidatorSpec extends Specification { + def objectUnderTest = new CpsValidatorImpl() + def 'Validating a valid string.'() { when: 'the string is validated using a valid name' - CpsValidator.validateNameCharacters('name-with-no-spaces') + objectUnderTest.validateNameCharacters('name-with-no-spaces') then: 'no exception is thrown' noExceptionThrown() } def 'Validating an invalid string.'() { when: 'the string is validated using an invalid name' - CpsValidator.validateNameCharacters(name) + objectUnderTest.validateNameCharacters(name) then: 'a data validation exception is thrown' def exceptionThrown = thrown(DataValidationException) and: 'the error was encountered at the following index in #scenario' @@ -44,5 +46,4 @@ class CpsValidatorSpec extends Specification { 'position 5' | 'name with spaces' || 'name with spaces invalid token encountered at position 5' 'position 9' | 'nameWith Space' || 'nameWith Space invalid token encountered at position 9' } - } diff --git a/cps-ri/src/test/resources/application.yml b/cps-ri/src/test/resources/application.yml index 18e6ed6243..e835b77b12 100644 --- a/cps-ri/src/test/resources/application.yml +++ b/cps-ri/src/test/resources/application.yml @@ -1,5 +1,6 @@ # ============LICENSE_START======================================================= # Copyright (C) 2021 Pantheon.tech +# Modifications 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. @@ -18,7 +19,7 @@ spring: jpa: ddl-auto: create - show-sql: true + show-sql: false properties: hibernate: enable_lazy_load_no_trans: true @@ -33,4 +34,4 @@ spring: initialization-mode: always liquibase: - change-log: classpath:changelog/changelog-master.yaml
\ No newline at end of file + change-log: classpath:changelog/changelog-master.yaml 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 a67dfe503a..56f424164f 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 @@ -30,7 +30,7 @@ import org.onap.cps.api.CpsAdminService; import org.onap.cps.api.CpsDataService; import org.onap.cps.spi.CpsAdminPersistenceService; import org.onap.cps.spi.model.Anchor; -import org.onap.cps.utils.CpsValidator; +import org.onap.cps.spi.utils.CpsValidator; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -41,53 +41,54 @@ public class CpsAdminServiceImpl implements CpsAdminService { private final CpsAdminPersistenceService cpsAdminPersistenceService; @Lazy private final CpsDataService cpsDataService; + private final CpsValidator cpsValidator; @Override public void createDataspace(final String dataspaceName) { - CpsValidator.validateNameCharacters(dataspaceName); + cpsValidator.validateNameCharacters(dataspaceName); cpsAdminPersistenceService.createDataspace(dataspaceName); } @Override public void deleteDataspace(final String dataspaceName) { - CpsValidator.validateNameCharacters(dataspaceName); + cpsValidator.validateNameCharacters(dataspaceName); cpsAdminPersistenceService.deleteDataspace(dataspaceName); } @Override public void createAnchor(final String dataspaceName, final String schemaSetName, final String anchorName) { - CpsValidator.validateNameCharacters(dataspaceName, schemaSetName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, schemaSetName, anchorName); cpsAdminPersistenceService.createAnchor(dataspaceName, schemaSetName, anchorName); } @Override public Collection<Anchor> getAnchors(final String dataspaceName) { - CpsValidator.validateNameCharacters(dataspaceName); + cpsValidator.validateNameCharacters(dataspaceName); return cpsAdminPersistenceService.getAnchors(dataspaceName); } @Override public Collection<Anchor> getAnchors(final String dataspaceName, final String schemaSetName) { - CpsValidator.validateNameCharacters(dataspaceName, schemaSetName); + cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); return cpsAdminPersistenceService.getAnchors(dataspaceName, schemaSetName); } @Override public Anchor getAnchor(final String dataspaceName, final String anchorName) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); return cpsAdminPersistenceService.getAnchor(dataspaceName, anchorName); } @Override public void deleteAnchor(final String dataspaceName, final String anchorName) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); cpsDataService.deleteDataNodes(dataspaceName, anchorName, OffsetDateTime.now()); cpsAdminPersistenceService.deleteAnchor(dataspaceName, anchorName); } @Override public Collection<String> queryAnchorNames(final String dataspaceName, final Collection<String> moduleNames) { - CpsValidator.validateNameCharacters(dataspaceName); + cpsValidator.validateNameCharacters(dataspaceName); final Collection<Anchor> anchors = cpsAdminPersistenceService.queryAnchors(dataspaceName, moduleNames); return anchors.stream().map(Anchor::getName).collect(Collectors.toList()); } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java index b6aa04be76..88ebe3bd01 100755 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java @@ -31,7 +31,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsAdminService; import org.onap.cps.api.CpsDataService; @@ -43,7 +43,7 @@ import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.Anchor; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DataNodeBuilder; -import org.onap.cps.utils.CpsValidator; +import org.onap.cps.spi.utils.CpsValidator; import org.onap.cps.utils.YangUtils; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -51,7 +51,7 @@ import org.springframework.stereotype.Service; @Service @Slf4j -@AllArgsConstructor +@RequiredArgsConstructor public class CpsDataServiceImpl implements CpsDataService { private static final String ROOT_NODE_XPATH = "/"; @@ -61,11 +61,12 @@ public class CpsDataServiceImpl implements CpsDataService { private final CpsAdminService cpsAdminService; private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache; private final NotificationService notificationService; + private final CpsValidator cpsValidator; @Override public void saveData(final String dataspaceName, final String anchorName, final String jsonData, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); final DataNode dataNode = buildDataNode(dataspaceName, anchorName, ROOT_NODE_XPATH, jsonData); cpsDataPersistenceService.storeDataNode(dataspaceName, anchorName, dataNode); processDataUpdatedEventAsync(dataspaceName, anchorName, ROOT_NODE_XPATH, CREATE, observedTimestamp); @@ -74,7 +75,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void saveData(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); final DataNode dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, parentNodeXpath, dataNode); processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, CREATE, observedTimestamp); @@ -83,7 +84,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void saveListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); final Collection<DataNode> listElementDataNodeCollection = buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService.addListElements(dataspaceName, anchorName, parentNodeXpath, @@ -94,7 +95,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void saveListElementsBatch(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<String> jsonDataList, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); final Collection<Collection<DataNode>> listElementDataNodeCollections = buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonDataList); cpsDataPersistenceService.addMultipleLists(dataspaceName, anchorName, parentNodeXpath, @@ -105,14 +106,14 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath, final FetchDescendantsOption fetchDescendantsOption) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); return cpsDataPersistenceService.getDataNode(dataspaceName, anchorName, xpath, fetchDescendantsOption); } @Override public void updateNodeLeaves(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); final DataNode dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService .updateDataLeaves(dataspaceName, anchorName, dataNode.getXpath(), dataNode.getLeaves()); @@ -124,7 +125,7 @@ public class CpsDataServiceImpl implements CpsDataService { final String parentNodeXpath, final String dataNodeUpdatesAsJson, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); final Collection<DataNode> dataNodeUpdates = buildDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodeUpdatesAsJson); @@ -159,7 +160,7 @@ public class CpsDataServiceImpl implements CpsDataService { public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); final DataNode dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService.updateDataNodeAndDescendants(dataspaceName, anchorName, dataNode); processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp); @@ -169,7 +170,7 @@ public class CpsDataServiceImpl implements CpsDataService { public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName, final Map<String, String> nodesJsonData, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); final List<DataNode> dataNodes = buildDataNodes(dataspaceName, anchorName, nodesJsonData); cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes); nodesJsonData.keySet().forEach(nodeXpath -> @@ -180,7 +181,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); final Collection<DataNode> newListElements = buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData); replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements, observedTimestamp); @@ -189,7 +190,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<DataNode> dataNodes, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, dataNodes); processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp); } @@ -197,7 +198,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void deleteDataNode(final String dataspaceName, final String anchorName, final String dataNodeXpath, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); cpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, dataNodeXpath); processDataUpdatedEventAsync(dataspaceName, anchorName, dataNodeXpath, DELETE, observedTimestamp); } @@ -205,7 +206,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void deleteDataNodes(final String dataspaceName, final String anchorName, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); processDataUpdatedEventAsync(dataspaceName, anchorName, ROOT_NODE_XPATH, DELETE, observedTimestamp); cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName); } @@ -213,7 +214,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void deleteListOrListElement(final String dataspaceName, final String anchorName, final String listNodeXpath, final OffsetDateTime observedTimestamp) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); cpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, listNodeXpath); processDataUpdatedEventAsync(dataspaceName, anchorName, listNodeXpath, DELETE, observedTimestamp); } 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 20b4a23a9b..b4890f4a71 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 @@ -24,7 +24,7 @@ package org.onap.cps.api.impl; import java.util.Collection; import java.util.Map; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.onap.cps.api.CpsAdminService; import org.onap.cps.api.CpsModuleService; import org.onap.cps.spi.CascadeDeleteAllowed; @@ -34,23 +34,24 @@ import org.onap.cps.spi.model.Anchor; 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.utils.CpsValidator; +import org.onap.cps.spi.utils.CpsValidator; import org.onap.cps.yang.YangTextSchemaSourceSetBuilder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service("CpsModuleServiceImpl") -@AllArgsConstructor +@RequiredArgsConstructor public class CpsModuleServiceImpl implements CpsModuleService { private final CpsModulePersistenceService cpsModulePersistenceService; private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache; private final CpsAdminService cpsAdminService; + private final CpsValidator cpsValidator; @Override public void createSchemaSet(final String dataspaceName, final String schemaSetName, final Map<String, String> yangResourcesNameToContentMap) { - CpsValidator.validateNameCharacters(dataspaceName, schemaSetName); + cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); final var yangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap); cpsModulePersistenceService.storeSchemaSet(dataspaceName, schemaSetName, yangResourcesNameToContentMap); @@ -61,7 +62,7 @@ public class CpsModuleServiceImpl implements CpsModuleService { public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName, final Map<String, String> newModuleNameToContentMap, final Collection<ModuleReference> allModuleReferences) { - CpsValidator.validateNameCharacters(dataspaceName, schemaSetName); + cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName, newModuleNameToContentMap, allModuleReferences); @@ -69,7 +70,7 @@ public class CpsModuleServiceImpl implements CpsModuleService { @Override public SchemaSet getSchemaSet(final String dataspaceName, final String schemaSetName) { - CpsValidator.validateNameCharacters(dataspaceName, schemaSetName); + cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); final var yangTextSchemaSourceSet = yangTextSchemaSourceSetCache .get(dataspaceName, schemaSetName); return SchemaSet.builder().name(schemaSetName).dataspaceName(dataspaceName) @@ -80,7 +81,7 @@ public class CpsModuleServiceImpl implements CpsModuleService { @Transactional public void deleteSchemaSet(final String dataspaceName, final String schemaSetName, final CascadeDeleteAllowed cascadeDeleteAllowed) { - CpsValidator.validateNameCharacters(dataspaceName, schemaSetName); + cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); final Collection<Anchor> anchors = cpsAdminService.getAnchors(dataspaceName, schemaSetName); if (!anchors.isEmpty() && isCascadeDeleteProhibited(cascadeDeleteAllowed)) { throw new SchemaSetInUseException(dataspaceName, schemaSetName); @@ -95,21 +96,21 @@ public class CpsModuleServiceImpl implements CpsModuleService { @Override public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName) { - CpsValidator.validateNameCharacters(dataspaceName); + cpsValidator.validateNameCharacters(dataspaceName); return cpsModulePersistenceService.getYangResourceModuleReferences(dataspaceName); } @Override public Collection<ModuleReference> getYangResourcesModuleReferences(final String dataspaceName, final String anchorName) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); return cpsModulePersistenceService.getYangResourceModuleReferences(dataspaceName, anchorName); } @Override public Collection<ModuleDefinition> getModuleDefinitionsByAnchorName(final String dataspaceName, final String anchorName) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); return cpsModulePersistenceService.getYangResourceDefinitions(dataspaceName, anchorName); } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java index c2003d6bf7..7b9043a608 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java @@ -1,6 +1,6 @@ /* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * ============LICENSE_START======================================================= + * 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. @@ -21,24 +21,25 @@ package org.onap.cps.api.impl; import java.util.Collection; +import lombok.RequiredArgsConstructor; import org.onap.cps.api.CpsQueryService; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; -import org.onap.cps.utils.CpsValidator; -import org.springframework.beans.factory.annotation.Autowired; +import org.onap.cps.spi.utils.CpsValidator; import org.springframework.stereotype.Service; @Service +@RequiredArgsConstructor public class CpsQueryServiceImpl implements CpsQueryService { - @Autowired - private CpsDataPersistenceService cpsDataPersistenceService; + private final CpsDataPersistenceService cpsDataPersistenceService; + private final CpsValidator cpsValidator; @Override public Collection<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath, final FetchDescendantsOption fetchDescendantsOption) { - CpsValidator.validateNameCharacters(dataspaceName, anchorName); + cpsValidator.validateNameCharacters(dataspaceName, anchorName); return cpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption); } } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java b/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java index fb881a97b6..0f620b0dd8 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java @@ -1,7 +1,8 @@ /* - * ============LICENSE_START======================================================= + * ============LICENSE_START======================================================= * Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2022 Bell Canada + * Modifications 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. @@ -23,11 +24,11 @@ package org.onap.cps.api.impl; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Map; +import lombok.RequiredArgsConstructor; import org.onap.cps.spi.CpsModulePersistenceService; -import org.onap.cps.utils.CpsValidator; +import org.onap.cps.spi.utils.CpsValidator; import org.onap.cps.yang.YangTextSchemaSourceSet; import org.onap.cps.yang.YangTextSchemaSourceSetBuilder; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; @@ -39,10 +40,11 @@ import org.springframework.stereotype.Service; */ @Service @CacheConfig(cacheNames = {"yangSchema"}) +@RequiredArgsConstructor public class YangTextSchemaSourceSetCache { - @Autowired - private CpsModulePersistenceService cpsModulePersistenceService; + private final CpsModulePersistenceService cpsModulePersistenceService; + private final CpsValidator cpsValidator; /** * Cache YangTextSchemaSourceSet. @@ -53,7 +55,7 @@ public class YangTextSchemaSourceSetCache { */ @Cacheable(key = "#p0.concat('-').concat(#p1)") public YangTextSchemaSourceSet get(final String dataspaceName, final String schemaSetName) { - CpsValidator.validateNameCharacters(dataspaceName, schemaSetName); + cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); final Map<String, String> yangResourceNameToContent = cpsModulePersistenceService.getYangSchemaResources(dataspaceName, schemaSetName); return YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent); @@ -71,7 +73,7 @@ public class YangTextSchemaSourceSetCache { @CanIgnoreReturnValue public YangTextSchemaSourceSet updateCache(final String dataspaceName, final String schemaSetName, final YangTextSchemaSourceSet yangTextSchemaSourceSet) { - CpsValidator.validateNameCharacters(dataspaceName, schemaSetName); + cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); return yangTextSchemaSourceSet; } @@ -84,7 +86,7 @@ public class YangTextSchemaSourceSetCache { */ @CacheEvict(key = "#p0.concat('-').concat(#p1)") public void removeFromCache(final String dataspaceName, final String schemaSetName) { - CpsValidator.validateNameCharacters(dataspaceName, schemaSetName); + cpsValidator.validateNameCharacters(dataspaceName, schemaSetName); // Spring provides implementation for removing object from cache } diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java b/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java index c77daafb38..19d5adef02 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java +++ b/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java @@ -45,6 +45,7 @@ public class DataNode implements Serializable { private String anchorName; private ModuleReference moduleReference; private String xpath; + @Setter(AccessLevel.PUBLIC) private String moduleNamePrefix; private Map<String, Object> leaves = Collections.emptyMap(); private Collection<String> xpathsChildren; diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/CpsTaskExecutionException.java b/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java index 3e8929d2e3..c7ce8fc926 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/CpsTaskExecutionException.java +++ b/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java @@ -18,27 +18,14 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.rest.exceptions; +package org.onap.cps.spi.utils; -import lombok.Getter; - -public class CpsTaskExecutionException extends RuntimeException { - - private static final long serialVersionUID = 1481520410918497454L; - - @Getter - final String details; +public interface CpsValidator { /** - * Constructor. + * Validate characters in names within cps. * - * @param message the error message - * @param details the error details - * @param cause the cause of the exception + * @param names names of data to be validated */ - public CpsTaskExecutionException(final String message, final String details, final Throwable cause) { - super(message, cause); - this.details = details; - } - -}
\ No newline at end of file + void validateNameCharacters(final String... names); +} diff --git a/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java b/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java index 7fe47be2da..4d0eb7d412 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java +++ b/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java @@ -42,16 +42,16 @@ public class CmHandleQueryRestParametersValidator { cmHandleQueryServiceParameters.getCmHandleQueryParameters().forEach( conditionApiProperty -> { if (Strings.isNullOrEmpty(conditionApiProperty.getConditionName())) { - throwDataValidationException("Missing 'conditionName' - please supply a valid name."); + throw createDataValidationException("Missing 'conditionName' - please supply a valid name."); } if (Arrays.stream(ValidQueryProperties.values()).noneMatch(validQueryProperty -> validQueryProperty.getQueryProperty().equals(conditionApiProperty.getConditionName()))) { - throwDataValidationException( + throw createDataValidationException( String.format("Wrong 'conditionName': %s - please supply a valid name.", conditionApiProperty.getConditionName())); } if (conditionApiProperty.getConditionParameters().isEmpty()) { - throwDataValidationException( + throw createDataValidationException( "Empty 'conditionsParameters' - please supply a valid condition parameter."); } conditionApiProperty.getConditionParameters().forEach( @@ -63,16 +63,16 @@ public class CmHandleQueryRestParametersValidator { private static void validateConditionParameter(final Map<String, String> conditionParameter) { if (conditionParameter.isEmpty()) { - throwDataValidationException( + throw createDataValidationException( "Empty 'conditionsParameter' - please supply a valid condition parameter."); } if (conditionParameter.size() > 1) { - throwDataValidationException("Too many name in one 'conditionsParameter' -" + throw createDataValidationException("Too many name in one 'conditionsParameter' -" + " please supply one name in one condition parameter."); } conditionParameter.forEach((key, value) -> { if (Strings.isNullOrEmpty(key)) { - throwDataValidationException( + throw createDataValidationException( "Missing 'conditionsParameterName' - please supply a valid name."); } }); @@ -86,7 +86,8 @@ public class CmHandleQueryRestParametersValidator { if (conditionProperty.containsKey("moduleName") && !conditionProperty.get("moduleName").isEmpty()) { return; } - throwDataValidationException("Wrong module condition property. - please supply a valid condition property."); + throw createDataValidationException("Wrong module condition property. - " + + "please supply a valid condition property."); } /** @@ -98,15 +99,15 @@ public class CmHandleQueryRestParametersValidator { return true; } if (conditionProperty.size() > 1) { - throwDataValidationException("Only one condition property is allowed for the CPS path query."); + throw createDataValidationException("Only one condition property is allowed for the CPS path query."); } if (!conditionProperty.containsKey("cpsPath")) { - throwDataValidationException( + throw createDataValidationException( "Wrong CPS path condition property. - expecting \"cpsPath\" as the condition property."); } final String cpsPath = conditionProperty.get("cpsPath"); if (cpsPath.isBlank()) { - throwDataValidationException( + throw createDataValidationException( "Wrong CPS path. - please supply a valid CPS path."); } if (cpsPath.contains("/additional-properties")) { @@ -117,8 +118,8 @@ public class CmHandleQueryRestParametersValidator { return true; } - private static void throwDataValidationException(final String details) { - throw new DataValidationException("Invalid Query Parameter.", details); + private static DataValidationException createDataValidationException(final String details) { + return new DataValidationException("Invalid Query Parameter.", details); } } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy index 41fcb29edd..2979c09866 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 @@ -24,30 +24,24 @@ package org.onap.cps.api.impl import org.onap.cps.api.CpsDataService import org.onap.cps.spi.CpsAdminPersistenceService -import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.Anchor +import org.onap.cps.spi.utils.CpsValidator import spock.lang.Specification import java.time.OffsetDateTime class CpsAdminServiceImplSpec extends Specification { def mockCpsAdminPersistenceService = Mock(CpsAdminPersistenceService) def mockCpsDataService = Mock(CpsDataService) - def objectUnderTest = new CpsAdminServiceImpl(mockCpsAdminPersistenceService, mockCpsDataService) + def mockCpsValidator = Mock(CpsValidator) + def objectUnderTest = new CpsAdminServiceImpl(mockCpsAdminPersistenceService, mockCpsDataService,mockCpsValidator) def 'Create dataspace method invokes persistence service.'() { when: 'create dataspace method is invoked' objectUnderTest.createDataspace('someDataspace') then: 'the persistence service method is invoked with same parameters' 1 * mockCpsAdminPersistenceService.createDataspace('someDataspace') - } - - def 'Create a dataspace with an invalid dataspace name.'() { - when: 'create dataspace method is invoked with incorrectly named dataspace' - objectUnderTest.createDataspace('Dataspace Name with spaces') - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsAdminPersistenceService.createDataspace(_) + and: 'the CpsValidator is called on the dataspaceName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace') } def 'Create anchor method invokes persistence service.'() { @@ -55,76 +49,44 @@ class CpsAdminServiceImplSpec extends Specification { objectUnderTest.createAnchor('someDataspace', 'someSchemaSet', 'someAnchorName') then: 'the persistence service method is invoked with same parameters' 1 * mockCpsAdminPersistenceService.createAnchor('someDataspace', 'someSchemaSet', 'someAnchorName') - } - - def 'Create an anchor with an invalid anchor name.'() { - when: 'create anchor method is invoked with incorrectly named dataspace' - objectUnderTest.createAnchor('someDataspace', 'someSchemaSet', 'Anchor Name With Spaces') - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsAdminPersistenceService.createAnchor(_, _, _) + and: 'the CpsValidator is called on the dataspaceName, schemaSetName and anchorName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someSchemaSet', 'someAnchorName') } def 'Retrieve all anchors for dataspace.'() { - given: 'that anchor is associated with the dataspace' + given: 'that an anchor is associated with the dataspace' def anchors = [new Anchor()] mockCpsAdminPersistenceService.getAnchors('someDataspace') >> anchors - expect: 'the collection provided by persistence service is returned as result' - objectUnderTest.getAnchors('someDataspace') == anchors - } - - def 'Retrieve all anchors with an invalid dataspace name.'() { - when: 'get anchors is invoked with an invalid dataspace name' - objectUnderTest.getAnchors('Dataspace name with spaces') - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'cps admin persistence get anchors is not invoked' - 0 * mockCpsAdminPersistenceService.getAnchors(_) + when: 'get Anchors is called for a dataspace name' + def result = objectUnderTest.getAnchors('someDataspace') + then: 'the collection provided by persistence service is returned as result' + result == anchors + and: 'the CpsValidator is called on the dataspaceName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace') } 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 + when: 'get anchors is called for a dataspace name and schema set name' + def result = objectUnderTest.getAnchors('someDataspace', 'someSchemaSet') + then: 'the collection provided by persistence service is returned as result' + result == anchors + and: 'the CpsValidator is called on the dataspaceName, schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someSchemaSet') } - def 'Retrieve all anchors for schema-set with invalid #scenario.'() { - when: 'the collection provided by persistence service is returned as result' - objectUnderTest.getAnchors(dataspaceName, schemaSetName) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'cps admin persistence get anchors is not invoked' - 0 * mockCpsAdminPersistenceService.getAnchors(_, _) - where: 'the following parameters are used' - scenario | dataspaceName | schemaSetName - 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName' - 'schema set name' | 'dataspaceName' | 'schema set name with spaces' - 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces' - } - def 'Retrieve anchor for dataspace and provided anchor name.'() { given: 'that anchor name is associated with the dataspace' Anchor anchor = new Anchor() mockCpsAdminPersistenceService.getAnchor('someDataspace','someAnchor') >> anchor - expect: 'the anchor provided by persistence service is returned as result' - assert objectUnderTest.getAnchor('someDataspace','someAnchor') == anchor - } - - def 'Retrieve anchor with invalid #scenario.'() { - when: 'get anchors is invoked with an invalid dataspace name' - objectUnderTest.getAnchor(dataspaceName, anchorName) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'cps admin persistence get anchor is not invoked' - 0 * mockCpsAdminPersistenceService.getAnchor(_, _) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' + when: 'get anchor is called for a dataspace name and anchor name' + def result = objectUnderTest.getAnchor('someDataspace','someAnchor') + then: 'the anchor provided by persistence service is returned as result' + result == anchor + and: 'the CpsValidator is called on the dataspaceName, anchorName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someAnchor') } def 'Delete anchor.'() { @@ -134,39 +96,19 @@ class CpsAdminServiceImplSpec extends Specification { 1 * mockCpsDataService.deleteDataNodes('someDataspace','someAnchor', _ as OffsetDateTime ) and: 'the persistence service method is invoked with same parameters to delete anchor' 1 * mockCpsAdminPersistenceService.deleteAnchor('someDataspace','someAnchor') - } - - def 'Delete anchor with invalid #scenario.'() { - when: 'delete anchor is invoked' - objectUnderTest.deleteAnchor(dataspaceName, anchorName) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'delete data nodes is invoked on the data service with expected parameters' - 0 * mockCpsDataService.deleteDataNodes(_,_, _ as OffsetDateTime ) - and: 'the persistence service method is invoked with same parameters to delete anchor' - 0 * mockCpsAdminPersistenceService.deleteAnchor(_,_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' + and: 'the CpsValidator is called on the dataspaceName, anchorName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someAnchor') } def 'Query all anchor identifiers for a dataspace and module names.'() { given: 'the persistence service is invoked with the expected parameters and returns a list of anchors' mockCpsAdminPersistenceService.queryAnchors('some-dataspace-name', ['some-module-name']) >> [new Anchor(name:'some-anchor-identifier')] - expect: 'get anchor identifiers returns the same anchor identifier returned by the persistence layer' - objectUnderTest.queryAnchorNames('some-dataspace-name', ['some-module-name']) == ['some-anchor-identifier'] - - } - - def 'Query all anchor identifiers for a dataspace and module names with an invalid dataspace name.'() { - when: 'delete anchor is invoked' - objectUnderTest.queryAnchorNames('some dataspace name', _ as Collection<String>) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'delete data nodes is not invoked' - 0 * mockCpsAdminPersistenceService.queryAnchors(_, _) + when: 'query anchor names is called using a dataspace name and module name' + def result = objectUnderTest.queryAnchorNames('some-dataspace-name', ['some-module-name']) + then: 'get anchor identifiers returns the same anchor identifier returned by the persistence layer' + result == ['some-anchor-identifier'] + and: 'the CpsValidator is called on the dataspaceName' + 1 * mockCpsValidator.validateNameCharacters('some-dataspace-name') } def 'Delete dataspace.'() { @@ -174,14 +116,7 @@ class CpsAdminServiceImplSpec extends Specification { objectUnderTest.deleteDataspace('someDataspace') then: 'associated persistence service method is invoked with correct parameter' 1 * mockCpsAdminPersistenceService.deleteDataspace('someDataspace') - } - - def 'Delete dataspace with invalid dataspace id.'() { - when: 'delete dataspace is invoked' - objectUnderTest.deleteDataspace('some dataspace name') - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'associated persistence service method is not invoked' - 0 * mockCpsAdminPersistenceService.deleteDataspace(_) + and: 'the CpsValidator is called on the dataspaceName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace') } } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy index a53706a06b..b60e7e86e0 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy @@ -35,6 +35,7 @@ import org.onap.cps.spi.model.DataNodeBuilder import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder import spock.lang.Specification +import org.onap.cps.spi.utils.CpsValidator import java.time.OffsetDateTime import java.util.stream.Collectors @@ -44,9 +45,10 @@ class CpsDataServiceImplSpec extends Specification { def mockCpsAdminService = Mock(CpsAdminService) def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache) def mockNotificationService = Mock(NotificationService) + def mockCpsValidator = Mock(CpsValidator) def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsAdminService, - mockYangTextSchemaSourceSetCache, mockNotificationService) + mockYangTextSchemaSourceSetCache, mockNotificationService, mockCpsValidator) def setup() { mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor @@ -67,26 +69,12 @@ class CpsDataServiceImplSpec extends Specification { then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.storeDataNode(dataspaceName, anchorName, { dataNode -> dataNode.xpath == '/test-tree' }) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/', Operation.CREATE, observedTimestamp) } - def 'Saving json data with invalid #scenario.'() { - when: 'save data method is invoked with invalid #scenario' - objectUnderTest.saveData(dataspaceName, anchorName, _ as String, observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsDataPersistenceService.storeDataNode(*_) - and: 'data updated event is not sent to notification service' - 0 * mockNotificationService.processDataUpdatedEvent(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - def 'Saving child data fragment under existing node.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') @@ -96,26 +84,12 @@ class CpsDataServiceImplSpec extends Specification { then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, '/test-tree', { dataNode -> dataNode.xpath == '/test-tree/branch[@name=\'New\']' }) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/test-tree', Operation.CREATE, observedTimestamp) } - def 'Saving child data fragment under existing node with invalid #scenario.'() { - when: 'save data method is invoked with test-tree and an invalid #scenario' - objectUnderTest.saveData(dataspaceName, anchorName, '/test-tree', _ as String, observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsDataPersistenceService.addChildDataNode(*_) - and: 'data updated event is not sent to notification service' - 0 * mockNotificationService.processDataUpdatedEvent(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - def 'Saving list element data fragment under existing node.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') @@ -132,6 +106,8 @@ class CpsDataServiceImplSpec extends Specification { } } ) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/test-tree', Operation.UPDATE, observedTimestamp) } @@ -166,20 +142,6 @@ class CpsDataServiceImplSpec extends Specification { thrown(DataValidationException) } - def 'Saving list element data fragment with invalid #scenario.'() { - when: 'save data method is invoked with an invalid #scenario' - objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', _ as String, observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'add list elements persistence method is not invoked' - 0 * mockCpsDataPersistenceService.addListElements(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - def 'Get data node with option #fetchDescendantsOption.'() { def xpath = '/xpath' def dataNode = new DataNodeBuilder().withXpath(xpath).build() @@ -191,20 +153,6 @@ class CpsDataServiceImplSpec extends Specification { fetchDescendantsOption << [FetchDescendantsOption.OMIT_DESCENDANTS, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS] } - def 'Get data node with option invalid #scenario.'() { - when: 'get data node is invoked with #scenario' - objectUnderTest.getDataNode(dataspaceName, anchorName, '/test-tree', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'get data node persistence service is not invoked' - 0 * mockCpsDataPersistenceService.getDataNode(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - def 'Update data node leaves: #scenario.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') @@ -212,6 +160,8 @@ class CpsDataServiceImplSpec extends Specification { objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp) then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, expectedNodeXpath, leaves) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, parentNodeXpath, Operation.UPDATE, observedTimestamp) where: 'following parameters were used' @@ -220,22 +170,6 @@ class CpsDataServiceImplSpec extends Specification { 'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']' | ['name': 'Name'] } - def 'Update data node with invalid #scenario.'() { - when: 'update data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath' - objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, '/', '{"test-tree": {"branch": []}}', observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsDataPersistenceService.updateDataLeaves(*_) - and: 'data updated event is not sent to notification service' - 0 * mockNotificationService.processDataUpdatedEvent(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - def 'Update list-element data node with : #scenario.'() { given: 'schema set for given anchor and dataspace references bookstore model' setupSchemaSetMocks('bookstore.yang') @@ -261,28 +195,12 @@ class CpsDataServiceImplSpec extends Specification { then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, "/bookstore/categories[@code='01']", ['name':'Romance', 'code': '01']) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'the data updated event is sent to the notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/bookstore', Operation.UPDATE, observedTimestamp) } - def 'Update Bookstore node leaves with invalid #scenario' () { - when: 'update data method is invoked with an invalid #scenario' - objectUnderTest.updateNodeLeavesAndExistingDescendantLeaves(dataspaceName, anchorName, - '/bookstore', _ as String, observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsDataPersistenceService.updateDataLeaves(*_) - and: 'the data updated event is not sent to the notification service' - 0 * mockNotificationService.processDataUpdatedEvent(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - - def 'Replace data node using singular data node: #scenario.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') @@ -293,6 +211,8 @@ class CpsDataServiceImplSpec extends Specification { { dataNode -> dataNode.xpath == expectedNodeXpath }) and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, parentNodeXpath, Operation.UPDATE, observedTimestamp) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'following parameters were used' scenario | parentNodeXpath | jsonData || expectedNodeXpath 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree' @@ -310,44 +230,14 @@ class CpsDataServiceImplSpec extends Specification { and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, nodesJsonData.keySet()[0], Operation.UPDATE, observedTimestamp) 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, nodesJsonData.keySet()[1], Operation.UPDATE, observedTimestamp) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'following parameters were used' scenario | nodesJsonData || expectedNodeXpath 'top level node' | ['/' : '{"test-tree": {"branch": []}}', '/test-tree' : '{"branch": [{"name":"Name"}]}'] || ["/test-tree", "/test-tree/branch[@name='Name']"] 'level 2 node' | ['/test-tree' : '{"branch": [{"name":"Name"}]}', '/test-tree/branch[@name=\'Name\']':'{"nest":{"name":"nestName"}}'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"] } - def 'Replace data node using singular data node with invalid #scenario.'() { - when: 'replace data method is invoked with invalid #scenario' - objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, '/', _ as String, observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsDataPersistenceService.updateDataNodeAndDescendants(*_) - and: 'data updated event is not sent to notification service' - 0 * mockNotificationService.processDataUpdatedEvent(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - - def 'Replace data node using multiple data nodes with invalid #scenario.'() { - when: 'replace data method is invoked with invalid #scenario' - objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, ['/': _ as String], observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(*_) - and: 'data updated event is not sent to notification service' - 0 * mockNotificationService.processDataUpdatedEvent(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - def 'Replace list content data fragment under parent node.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') @@ -364,6 +254,8 @@ class CpsDataServiceImplSpec extends Specification { } } ) + and: 'the CpsValidator is called on the dataspaceName and AnchorName twice' + 2 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/test-tree', Operation.UPDATE, observedTimestamp) } @@ -378,22 +270,6 @@ class CpsDataServiceImplSpec extends Specification { thrown(DataValidationException) } - def 'Replace whole list content with an invalid #scenario.'() { - when: 'replace list data method is invoked with invalid #scenario' - objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', _ as Collection<DataNode>, observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsDataPersistenceService.replaceListContent(*_) - and: 'data updated event is not sent to notification service' - 0 * mockNotificationService.processDataUpdatedEvent(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - def 'Delete list element under existing node.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') @@ -401,27 +277,12 @@ class CpsDataServiceImplSpec extends Specification { objectUnderTest.deleteListOrListElement(dataspaceName, anchorName, '/test-tree/branch', observedTimestamp) then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, '/test-tree/branch') + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/test-tree/branch', Operation.DELETE, observedTimestamp) } - - def 'Delete list element with an invalid #scenario.'() { - when: 'delete list data method is invoked with with invalid #scenario' - objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsDataPersistenceService.deleteListDataNode(*_) - and: 'data updated event is not sent to notification service' - 0 * mockNotificationService.processDataUpdatedEvent(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - def 'Delete data node under anchor and dataspace.'() { given: 'schema set for given anchor and dataspace references test tree model' setupSchemaSetMocks('test-tree.yang') @@ -429,26 +290,12 @@ class CpsDataServiceImplSpec extends Specification { objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp) then: 'the persistence service method is invoked with the correct parameters' 1 * mockCpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, '/data-node') + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/data-node', Operation.DELETE, observedTimestamp) } - def 'Delete data node with an invalid #scenario.'() { - when: 'delete data node method is invoked with invalid #scenario' - objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsDataPersistenceService.deleteDataNode(*_) - and: 'data updated event is not sent to notification service' - 0 * mockNotificationService.processDataUpdatedEvent(*_) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - def 'Delete all data nodes for a given anchor and dataspace.'() { given: 'schema set for given anchor and dataspace references test tree model' setupSchemaSetMocks('test-tree.yang') @@ -456,6 +303,8 @@ class CpsDataServiceImplSpec extends Specification { objectUnderTest.deleteDataNodes(dataspaceName, anchorName, observedTimestamp) then: 'data updated event is sent to notification service before the delete' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/', Operation.DELETE, observedTimestamp) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) and: 'the persistence service method is invoked with the correct parameters' 1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName) } 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 429de7d51b..690578ea05 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 @@ -1,5 +1,5 @@ /* - * ============LICENSE_START======================================================= + * ============LICENSE_START======================================================= * Copyright (C) 2020-2022 Nordix Foundation * Modifications Copyright (C) 2020-2021 Pantheon.tech * Modifications Copyright (C) 2020-2022 Bell Canada. @@ -25,9 +25,9 @@ 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.DataValidationException import org.onap.cps.spi.exceptions.ModelValidationException import org.onap.cps.spi.exceptions.SchemaSetInUseException +import org.onap.cps.spi.utils.CpsValidator import org.onap.cps.spi.model.Anchor import org.onap.cps.spi.model.ModuleReference import org.onap.cps.yang.YangTextSchemaSourceSetBuilder @@ -40,8 +40,9 @@ class CpsModuleServiceImplSpec extends Specification { def mockCpsModulePersistenceService = Mock(CpsModulePersistenceService) def mockCpsAdminService = Mock(CpsAdminService) def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache) + def mockCpsValidator = Mock(CpsValidator) - def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAdminService) + def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAdminService, mockCpsValidator) def 'Create schema set.'() { given: 'Valid yang resource as name-to-content map' @@ -50,20 +51,8 @@ class CpsModuleServiceImplSpec extends Specification { objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap) then: 'Parameters are validated and processing is delegated to persistence service' 1 * mockCpsModulePersistenceService.storeSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap) - } - - def 'Create a schema set with an invalid #scenario.'() { - when: 'create dataspace method is invoked with incorrectly named dataspace' - objectUnderTest.createSchemaSet(dataspaceName, schemaSetName, _ as Map<String, String>) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsModulePersistenceService.storeSchemaSet(_, _, _) - where: 'the following parameters are used' - scenario | dataspaceName | schemaSetName - 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName' - 'schema set name name' | 'dataspaceName' | 'schema set name with spaces' - 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces' + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someSchemaSet') } def 'Create schema set from new modules and existing modules.'() { @@ -74,20 +63,8 @@ class CpsModuleServiceImplSpec extends Specification { objectUnderTest.createSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference) then: 'processing is delegated to persistence service' 1 * mockCpsModulePersistenceService.storeSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference) - } - - def 'Create schema set from new modules and existing modules with invalid #scenario.'() { - when: 'create dataspace method is invoked with incorrectly named dataspace' - objectUnderTest.createSchemaSetFromModules(dataspaceName, schemaSetName, _ as Map<String, String>, _ as Collection<ModuleReference>) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsModulePersistenceService.storeSchemaSetFromModules(_, _, _) - where: 'the following parameters are used' - scenario | dataspaceName | schemaSetName - 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName' - 'schema set name name' | 'dataspaceName' | 'schema set name with spaces' - 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces' + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('someDataspaceName', 'someSchemaSetName') } def 'Create schema set from invalid resources'() { @@ -110,20 +87,8 @@ class CpsModuleServiceImplSpec extends Specification { result.getName().contains('someSchemaSet') result.getDataspaceName().contains('someDataspace') result.getModuleReferences().contains(new ModuleReference('stores', '2020-09-15', 'org:onap:ccsdk:sample')) - } - - def 'Get a schema set with an invalid #scenario'() { - when: 'create dataspace method is invoked with incorrectly named dataspace' - objectUnderTest.getSchemaSet(dataspaceName, schemaSetName) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the yang resource cache is not invoked' - 0 * mockYangTextSchemaSourceSetCache.get(_, _) - where: 'the following parameters are used' - scenario | dataspaceName | schemaSetName - 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName' - 'schema set name' | 'dataspaceName' | 'schema set name with spaces' - 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces' + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someSchemaSet') } def 'Delete schema-set when cascade is allowed.'() { @@ -140,6 +105,8 @@ class CpsModuleServiceImplSpec extends Specification { 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my-dataspace', 'my-schemaset') and: 'orphan yang resources are deleted' 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules() + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('my-dataspace', _) where: 'following parameters are used' numberOfAnchors << [0, 3] } @@ -157,6 +124,8 @@ class CpsModuleServiceImplSpec extends Specification { 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my-dataspace', 'my-schemaset') and: 'orphan yang resources are deleted' 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules() + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('my-dataspace', 'my-schemaset') } def 'Delete schema-set when cascade is prohibited and schema-set has anchors.'() { @@ -168,26 +137,6 @@ class CpsModuleServiceImplSpec extends Specification { thrown(SchemaSetInUseException) } - def 'Delete a schema set with an invalid #scenario.'() { - when: 'create dataspace method is invoked with incorrectly named dataspace' - objectUnderTest.deleteSchemaSet(dataspaceName, schemaSetName, CASCADE_DELETE_ALLOWED) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'anchor deletion is called 0 times' - 0 * mockCpsAdminService.deleteAnchor(_, _) - and: 'the delete schema set persistence service method is not invoked' - 0 * mockCpsModulePersistenceService.deleteSchemaSet(_, _, _) - and: 'schema set will be removed from the cache is not invoked' - 0 * mockYangTextSchemaSourceSetCache.removeFromCache(_, _) - and: 'orphan yang resources are deleted is not invoked' - 0 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules() - where: 'the following parameters are used' - scenario | dataspaceName | schemaSetName - 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName' - 'schema set name name' | 'dataspaceName' | 'schema set name with spaces' - 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces' - } - def createAnchors(int anchorCount) { def anchors = [] (0..<anchorCount).each { anchors.add(new Anchor("my-anchor-$it", 'my-dataspace', 'my-schemaset')) } @@ -198,40 +147,24 @@ class CpsModuleServiceImplSpec extends Specification { given: 'an already present module reference' def moduleReferences = [new ModuleReference('some module name','some revision name')] mockCpsModulePersistenceService.getYangResourceModuleReferences('someDataspaceName') >> moduleReferences - expect: 'the list provided by persistence service is returned as result' - objectUnderTest.getYangResourceModuleReferences('someDataspaceName') == moduleReferences - } - - def 'Get all yang resources module references given an invalid dataspace name.'() { - when: 'the get yang resources module references method is invoked with an invalid dataspace name' - objectUnderTest.getYangResourceModuleReferences('dataspace name with spaces') - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsModulePersistenceService.getYangResourceModuleReferences(_) + when: 'get yang resource module references is called' + def result = objectUnderTest.getYangResourceModuleReferences('someDataspaceName') + then: 'the list provided by persistence service is returned as result' + result == moduleReferences + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('someDataspaceName') } - 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()] mockCpsModulePersistenceService.getYangResourceModuleReferences('someDataspaceName', 'someAnchorName') >> moduleReferences - expect: 'the list provided by persistence service is returned as result' - objectUnderTest.getYangResourcesModuleReferences('someDataspaceName', 'someAnchorName') == moduleReferences - } - - def 'Get all yang resources module references given an invalid #scenario.'() { - when: 'the get yang resources module references method is invoked with invalid #scenario' - objectUnderTest.getYangResourcesModuleReferences(dataspaceName, anchorName) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service method is not invoked' - 0 * mockCpsModulePersistenceService.getYangResourceModuleReferences(_, _) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' + when: 'get yang resource module references is called for dataspace name and anchor name' + def result = objectUnderTest.getYangResourcesModuleReferences('someDataspaceName', 'someAnchorName') + then: 'the list provided by persistence service is returned as result' + result == moduleReferences + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('someDataspaceName', 'someAnchorName') } def 'Identifying new module references'(){ @@ -248,5 +181,7 @@ class CpsModuleServiceImplSpec extends Specification { objectUnderTest.getModuleDefinitionsByAnchorName('some-dataspace-name', 'some-anchor-name') then: 'CPS module persistence service is invoked the correct number of times' 1 * mockCpsModulePersistenceService.getYangResourceDefinitions('some-dataspace-name', 'some-anchor-name') + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('some-dataspace-name', 'some-anchor-name') } } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy index b7fec85119..8b232b420d 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy @@ -1,5 +1,5 @@ /* - * ============LICENSE_START======================================================= + * ============LICENSE_START======================================================= * Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,17 +22,14 @@ package org.onap.cps.api.impl import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.FetchDescendantsOption -import org.onap.cps.spi.exceptions.DataValidationException +import org.onap.cps.spi.utils.CpsValidator import spock.lang.Specification class CpsQueryServiceImplSpec extends Specification { def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService) + def mockCpsValidator = Mock(CpsValidator) - def objectUnderTest = new CpsQueryServiceImpl() - - def setup() { - objectUnderTest.cpsDataPersistenceService = mockCpsDataPersistenceService - } + def objectUnderTest = new CpsQueryServiceImpl(mockCpsDataPersistenceService, mockCpsValidator) def 'Query data nodes by cps path with #fetchDescendantsOption.'() { given: 'a dataspace name, an anchor name and a cps path' @@ -43,22 +40,10 @@ class CpsQueryServiceImplSpec extends Specification { objectUnderTest.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption) then: 'the persistence service is called once with the correct parameters' 1 * mockCpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption) + and: 'the CpsValidator is called on the dataspaceName, schemaSetName and anchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'all fetch descendants options are supported' fetchDescendantsOption << [FetchDescendantsOption.OMIT_DESCENDANTS, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS] } - def 'Query data nodes by cps path with invalid #scenario.'() { - when: 'queryDataNodes is invoked' - objectUnderTest.queryDataNodes(dataspaceName, anchorName, '/cps-path', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) - then: 'a data validation exception is thrown' - thrown(DataValidationException) - and: 'the persistence service is not invoked' - 0 * mockCpsDataPersistenceService.queryDataNodes(_, _, _, _) - where: 'the following parameters are used' - scenario | dataspaceName | anchorName - 'dataspace name' | 'dataspace names with spaces' | 'anchorName' - 'anchor name' | 'dataspaceName' | 'anchor name with spaces' - 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces' - } - } 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 52389522f8..2fc85aa5a4 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,6 +1,6 @@ /*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021 Nordix Foundation.
+ * Copyright (C) 2021-2022 Nordix Foundation.
* Modifications Copyright (C) 2021-2022 Bell Canada.
* Modifications Copyright (C) 2021 Pantheon.tech
* ================================================================================
@@ -8,7 +8,7 @@ * 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
+ * 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,
@@ -28,6 +28,7 @@ import org.onap.cps.notification.NotificationService import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.model.Anchor
+import org.onap.cps.spi.utils.CpsValidator
import org.onap.cps.utils.YangUtils
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import spock.lang.Specification
@@ -38,10 +39,11 @@ class E2ENetworkSliceSpec extends Specification { def mockCpsAdminService = Mock(CpsAdminService)
def mockNotificationService = Mock(NotificationService)
def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
+ def mockCpsValidator = Mock(CpsValidator)
def cpsModuleServiceImpl = new CpsModuleServiceImpl(mockModuleStoreService,
- mockYangTextSchemaSourceSetCache,mockCpsAdminService )
+ mockYangTextSchemaSourceSetCache, mockCpsAdminService, mockCpsValidator)
def cpsDataServiceImpl = new CpsDataServiceImpl(mockDataStoreService, mockCpsAdminService,
- mockYangTextSchemaSourceSetCache, mockNotificationService)
+ mockYangTextSchemaSourceSetCache, mockNotificationService, mockCpsValidator)
def dataspaceName = 'someDataspace'
def anchorName = 'someAnchor'
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy index 06c675a255..a9f50ee5b0 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy @@ -1,5 +1,5 @@ /* - * ============LICENSE_START======================================================= + * ============LICENSE_START======================================================= * Copyright (C) 2022 Bell Canada * Modifications Copyright (C) 2022 Nordix Foundation * ================================================================================ @@ -23,7 +23,6 @@ package org.onap.cps.api.impl import org.onap.cps.TestUtils import org.onap.cps.spi.CpsModulePersistenceService -import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder import org.spockframework.spring.SpringBean @@ -35,6 +34,8 @@ import org.springframework.cache.annotation.EnableCaching import org.springframework.cache.caffeine.CaffeineCacheManager import org.springframework.test.context.ContextConfiguration import spock.lang.Specification +import org.onap.cps.spi.utils.CpsValidator + @SpringBootTest @EnableCaching @@ -44,6 +45,9 @@ class YangTextSchemaSourceSetCacheSpec extends Specification { @SpringBean CpsModulePersistenceService mockModuleStoreService = Mock() + @SpringBean + CpsValidator mockCpsValidator = Mock(CpsValidator) + @Autowired YangTextSchemaSourceSetCache objectUnderTest @@ -74,6 +78,8 @@ class YangTextSchemaSourceSetCacheSpec extends Specification { assert cachedValue.getModuleReferences() == expectedYangTextSchemaSourceSet.getModuleReferences() and: 'the response is as expected' assert result.getModuleReferences() == expectedYangTextSchemaSourceSet.getModuleReferences() + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('my-dataspace', 'my-schemaset') } def 'Cache Hit: Respond from cache'() { @@ -90,20 +96,6 @@ class YangTextSchemaSourceSetCacheSpec extends Specification { 0 * mockModuleStoreService.getYangSchemaResources(_, _) } - def 'Cache Hit: with invalid #scenario'() { - when: 'schema-set information is asked' - objectUnderTest.get(dataspaceName, schemaSetName) - then: 'an data validation exception is thrown' - thrown(DataValidationException) - and: 'module persistence is not invoked' - 0 * mockModuleStoreService.getYangSchemaResources(_, _) - where: 'the following parameters are used' - scenario | dataspaceName | schemaSetName - 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName' - 'schema set name' | 'dataspaceName' | 'schema set name with spaces' - 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces' - } - def 'Cache Update: when no data exist in the cache'() { given: 'a schema set exists' def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang') @@ -113,23 +105,8 @@ class YangTextSchemaSourceSetCacheSpec extends Specification { then: 'cached value is same as expected' def cachedValue = getCachedValue('my-dataspace', 'my-schemaset') cachedValue.getModuleReferences() == yangTextSchemaSourceSet.getModuleReferences() - } - - def 'Cache Update: with invalid #scenario'() { - given: 'a schema set exists' - def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang') - def yangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap) - when: 'schema-set information is asked' - objectUnderTest.updateCache(dataspaceName, schemaSetName, yangTextSchemaSourceSet) - then: 'an data validation exception is thrown' - thrown(DataValidationException) - and: 'module persistence is not invoked' - 0 * mockModuleStoreService.getYangSchemaResources(_, _) - where: 'the following parameters are used' - scenario | dataspaceName | schemaSetName - 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName' - 'schema set name' | 'dataspaceName' | 'schema set name with spaces' - 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces' + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('my-dataspace', 'my-schemaset') } def 'Cache Evict:with invalid #scenario'() { @@ -143,18 +120,8 @@ class YangTextSchemaSourceSetCacheSpec extends Specification { objectUnderTest.removeFromCache('my-dataspace', 'my-schemaset') then: 'cached does not have value' assert getCachedValue('my-dataspace', 'my-schemaset') == null - } - - def 'Cache Evict: remove when exist'() { - when: 'cache is evicted for schemaset' - objectUnderTest.removeFromCache(dataspaceName, schemaSetName) - then: 'an data validation exception is thrown' - thrown(DataValidationException) - where: 'the following parameters are used' - scenario | dataspaceName | schemaSetName - 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName' - 'schema set name' | 'dataspaceName' | 'schema set name with spaces' - 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces' + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('my-dataspace', 'my-schemaset') } def 'Cache Evict: remove when does not exist'() { @@ -164,6 +131,8 @@ class YangTextSchemaSourceSetCacheSpec extends Specification { objectUnderTest.removeFromCache('my-dataspace', 'my-schemaset') then: 'cached does not have value' assert getCachedValue('my-dataspace', 'my-schemaset') == null + and: 'the CpsValidator is called on the dataspaceName and schemaSetName' + 1 * mockCpsValidator.validateNameCharacters('my-dataspace', 'my-schemaset') } def getCachedValue(dataSpace, schemaSet) { diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy index d5dcb7fc5c..c40ffa9a35 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy @@ -103,6 +103,11 @@ class CmHandleQueryRestParametersValidatorSpec extends Specification { 'cpsPath not supplied' | ['cpsPath':''] || 'Wrong CPS path. - please supply a valid CPS path.' } + def 'No conditions.'() { + expect: 'no conditions always returns true' + CmHandleQueryRestParametersValidator.validateCpsPathConditionProperties([:]) == true + } + def 'Validate CmHandle where #scenario.'() { when: 'the validator is called on a cps path condition property' def result = CmHandleQueryRestParametersValidator.validateCpsPathConditionProperties(['cpsPath':cpsPath]) diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy index 461014418e..e205a19eed 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy @@ -20,7 +20,7 @@ package org.onap.cps.utils - +import com.fasterxml.jackson.core.JsonProcessingException import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature import groovy.json.JsonSlurper @@ -44,6 +44,19 @@ class JsonObjectMapperSpec extends Specification { assert contentMap.'test:bookstore'.'bookstore-name' == 'Chapters' } + def 'Map a structured object to json String error.'() { + given: 'some object' + def object = new Object() + and: 'the Object mapper throws an exception' + spiedObjectMapper.writeValueAsString(object) >> { throw new JsonProcessingException('Sample problem'){} } + when: 'attempting to convert the object to a string' + jsonObjectMapper.asJsonString(object); + then: 'a Data Validation Exception is thrown' + def thrown = thrown(DataValidationException) + and: 'the details containing the original error message' + assert thrown.details == 'Sample problem' + } + def 'Map a structurally compatible object to class object of specific class type T.'() { given: 'a map object model' def contentMap = new JsonSlurper().parseText(TestUtils.getResourceFileContent('bookstore.json')) @@ -61,7 +74,7 @@ class JsonObjectMapperSpec extends Specification { given: 'Unstructured json string' def content = '{ "nest": { "birds": "bird"] } }' when: 'mapping json string to given class type' - def contentMap = jsonObjectMapper.convertJsonString(content, Map); + jsonObjectMapper.convertJsonString(content, Map); then: 'an exception is thrown' thrown(DataValidationException) } |