From dbed81b6cece07c8950fa2f32f6f05eba3bb991e Mon Sep 17 00:00:00 2001 From: ToineSiebelink Date: Thu, 13 Jun 2024 16:52:45 +0100 Subject: Introducing NCMP Facades - NetworkCmProxyDataService was polluted with many non-data operations - it is now split into: 1) NetworkCmProxyFacade, a single thin facade for the (main) controler to redirect calls to the correct services 2) CmHandleRegistrationService(Impl), methods related to registration only - introduced NetworkCmProxyInventoryFacade for the invenoty controller - renamed some services for consitency and clarification - Use facade to acces ncmp data request handlers (instead of direct from controller) - remove unnecesarry wrappings between request handlers and facade - split facades according to names: data & inventory (the REST controllers are not split properly so I think one rest controller will end up needing both facades) Issue-ID: CPS-2263 Change-Id: I250732aa16ec28b43ff642d2adf10ba36f67290e Signed-off-by: ToineSiebelink --- .../rest/controller/NetworkCmProxyController.java | 55 +- .../NetworkCmProxyInventoryController.java | 12 +- .../controller/NetworkCmProxyControllerSpec.groovy | 143 ++---- .../NetworkCmProxyInventoryControllerSpec.groovy | 21 +- .../NetworkCmProxyRestExceptionHandlerSpec.groovy | 17 +- .../api/NetworkCmProxyCmHandleQueryService.java | 71 --- .../cps/ncmp/api/NetworkCmProxyDataService.java | 214 -------- .../api/ParameterizedCmHandleQueryService.java | 72 +++ .../ncmp/api/impl/CmHandleRegistrationService.java | 404 +++++++++++++++ ...CmHandleRegistrationServicePropertyHandler.java | 246 +++++++++ .../api/impl/NcmpCachedResourceRequestHandler.java | 12 +- .../ncmp/api/impl/NcmpDatastoreRequestHandler.java | 39 +- .../NcmpPassthroughResourceRequestHandler.java | 48 +- .../NetworkCmProxyCmHandleQueryServiceImpl.java | 286 ----------- .../api/impl/NetworkCmProxyDataServiceImpl.java | 568 --------------------- .../NetworkCmProxyDataServicePropertyHandler.java | 243 --------- .../cps/ncmp/api/impl/NetworkCmProxyFacade.java | 129 +++++ .../api/impl/NetworkCmProxyInventoryFacade.java | 207 ++++++++ .../ParameterizedCmHandleQueryServiceImpl.java | 286 +++++++++++ .../cps/ncmp/api/impl/client/DmiRestClient.java | 36 +- .../ncmp/api/impl/inventory/CmHandleQueries.java | 104 ---- .../api/impl/inventory/CmHandleQueriesImpl.java | 192 ------- .../api/impl/inventory/CmHandleQueryService.java | 105 ++++ .../impl/inventory/CmHandleQueryServiceImpl.java | 192 +++++++ .../impl/inventory/InventoryPersistenceImpl.java | 9 +- .../impl/inventory/sync/ModuleOperationsUtils.java | 23 +- .../api/impl/inventory/sync/ModuleSyncService.java | 6 +- .../api/impl/operations/DmiDataOperations.java | 41 +- .../api/impl/operations/DmiModelOperations.java | 3 +- .../dmiavailability/DmiPluginWatchDog.java | 6 +- ...leRegistrationServicePropertyHandlerSpec.groovy | 296 +++++++++++ .../impl/CmHandleRegistrationServiceSpec.groovy | 458 +++++++++++++++++ .../NcmpCachedResourceRequestHandlerSpec.groovy | 64 +++ .../impl/NcmpDatastoreRequestHandlerSpec.groovy | 67 +-- .../NetworkCmProxyCmHandleQueryServiceSpec.groovy | 223 -------- ...rkCmProxyDataServiceImplRegistrationSpec.groovy | 421 --------------- .../impl/NetworkCmProxyDataServiceImplSpec.groovy | 413 --------------- ...orkCmProxyDataServicePropertyHandlerSpec.groovy | 285 ----------- .../ncmp/api/impl/NetworkCmProxyFacadeSpec.groovy | 108 ++++ .../impl/NetworkCmProxyInventoryFacadeSpec.groovy | 226 ++++++++ .../ParameterizedCmHandleQueryServiceSpec.groovy | 222 ++++++++ .../ncmp/api/impl/client/DmiRestClientSpec.groovy | 65 ++- .../impl/inventory/CmHandleQueriesImplSpec.groovy | 206 -------- .../inventory/CmHandleQueryServiceImplSpec.groovy | 206 ++++++++ .../inventory/InventoryPersistenceImplSpec.groovy | 2 +- .../sync/ModuleOperationsUtilsSpec.groovy | 6 +- .../inventory/sync/ModuleSyncServiceSpec.groovy | 4 +- .../impl/operations/DmiDataOperationsSpec.groovy | 14 +- .../impl/operations/DmiModelOperationsSpec.groovy | 16 +- .../dmiavailability/DmiPluginWatchDogSpec.groovy | 9 +- .../integration/base/CpsIntegrationSpecBase.groovy | 37 +- .../NcmpBearerTokenPassthroughSpec.groovy | 16 +- .../functional/NcmpCmHandleCreateSpec.groovy | 19 +- .../functional/NcmpCmHandleUpgradeSpec.groovy | 14 +- 54 files changed, 3543 insertions(+), 3644 deletions(-) delete mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java delete mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/ParameterizedCmHandleQueryService.java create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/CmHandleRegistrationService.java create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServicePropertyHandler.java delete mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java delete mode 100755 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java delete mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyFacade.java create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyInventoryFacade.java create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/ParameterizedCmHandleQueryServiceImpl.java delete mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java delete mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryService.java create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryServiceImpl.java create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServicePropertyHandlerSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServiceSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NcmpCachedResourceRequestHandlerSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyFacadeSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyInventoryFacadeSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/ParameterizedCmHandleQueryServiceSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImplSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryServiceImplSpec.groovy diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java index 58d6ce7108..726f4e4e2c 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java @@ -38,10 +38,8 @@ import java.util.Map; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; -import org.onap.cps.ncmp.api.impl.NcmpCachedResourceRequestHandler; -import org.onap.cps.ncmp.api.impl.NcmpDatastoreRequestHandler; -import org.onap.cps.ncmp.api.impl.NcmpPassthroughResourceRequestHandler; +import org.onap.cps.ncmp.api.impl.NetworkCmProxyFacade; +import org.onap.cps.ncmp.api.impl.NetworkCmProxyInventoryFacade; import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; import org.onap.cps.ncmp.api.impl.exception.InvalidDatastoreException; import org.onap.cps.ncmp.api.impl.inventory.CompositeState; @@ -79,14 +77,15 @@ import org.springframework.web.bind.annotation.RestController; public class NetworkCmProxyController implements NetworkCmProxyApi { private static final String NO_BODY = null; - private final NetworkCmProxyDataService networkCmProxyDataService; + private final NetworkCmProxyFacade networkCmProxyFacade; + private final NetworkCmProxyInventoryFacade networkCmProxyInventoryFacade; private final JsonObjectMapper jsonObjectMapper; private final DeprecationHelper deprecationHelper; private final NcmpRestInputMapper ncmpRestInputMapper; private final CmHandleStateMapper cmHandleStateMapper; - private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler; - private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler; + private final DataOperationRequestMapper dataOperationRequestMapper; + @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_CM_HANDLE) private final Map trustLevelPerCmHandle; @@ -111,10 +110,9 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String topicParamInQuery, final Boolean includeDescendants, final String authorization) { - final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler = getNcmpDatastoreRequestHandler(datastoreName); final CmResourceAddress cmResourceAddress = new CmResourceAddress(datastoreName, cmHandle, resourceIdentifier); - final Object result = ncmpDatastoreRequestHandler.executeRequest(cmResourceAddress, optionsParamInQuery, - topicParamInQuery, includeDescendants, authorization); + final Object result = networkCmProxyFacade.getResourceDataForCmHandle(cmResourceAddress, optionsParamInQuery, + topicParamInQuery, includeDescendants, authorization); return ResponseEntity.ok(result); } @@ -122,7 +120,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { public ResponseEntity executeDataOperationForCmHandles(final String topicParamInQuery, final DataOperationRequest dataOperationRequest, final String authorization) { - final Object result = ncmpPassthroughResourceRequestHandler.executeRequest(topicParamInQuery, + final Object result = networkCmProxyFacade.executeDataOperationForCmHandles(topicParamInQuery, dataOperationRequestMapper.toDataOperationRequest(dataOperationRequest), authorization); return ResponseEntity.ok(result); } @@ -147,7 +145,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String topicParamInQuery, final Boolean includeDescendants) { validateDataStore(OPERATIONAL, datastoreName); - final Collection dataNodes = ncmpCachedResourceRequestHandler.executeRequest(cmHandle, cpsPath, + final Collection dataNodes = networkCmProxyFacade.queryResourceDataForCmHandle(cmHandle, cpsPath, includeDescendants); return ResponseEntity.ok(dataNodes); } @@ -174,7 +172,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { validateDataStore(PASSTHROUGH_RUNNING, datastoreName); - final Object responseObject = networkCmProxyDataService + final Object responseObject = networkCmProxyFacade .writeResourceDataPassThroughRunningForCmHandle( cmHandle, resourceIdentifier, PATCH, jsonObjectMapper.asJsonString(requestBody), contentType, authorization); @@ -201,7 +199,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String authorization) { validateDataStore(PASSTHROUGH_RUNNING, datastoreName); - networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, + networkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle(cmHandle, resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType, authorization); return new ResponseEntity<>(HttpStatus.CREATED); } @@ -227,7 +225,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String authorization) { validateDataStore(PASSTHROUGH_RUNNING, datastoreName); - networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, + networkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle(cmHandle, resourceIdentifier, UPDATE, jsonObjectMapper.asJsonString(requestBody), contentType, authorization); return new ResponseEntity<>(HttpStatus.OK); } @@ -251,7 +249,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { validateDataStore(PASSTHROUGH_RUNNING, datastoreName); - networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, + networkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle(cmHandle, resourceIdentifier, DELETE, NO_BODY, contentType, authorization); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @@ -268,7 +266,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final CmHandleQueryParameters cmHandleQueryParameters) { final CmHandleQueryApiParameters cmHandleQueryApiParameters = deprecationHelper.mapOldConditionProperties(cmHandleQueryParameters); - final Collection cmHandles = networkCmProxyDataService + final Collection cmHandles = networkCmProxyInventoryFacade .executeCmHandleSearch(cmHandleQueryApiParameters); final List outputCmHandles = cmHandles.stream().map(this::toRestOutputCmHandle).collect(Collectors.toList()); @@ -287,7 +285,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final CmHandleQueryApiParameters cmHandleQueryApiParameters = jsonObjectMapper.convertToValueType(cmHandleQueryParameters, CmHandleQueryApiParameters.class); final Collection cmHandleIds - = networkCmProxyDataService.executeCmHandleIdSearch(cmHandleQueryApiParameters); + = networkCmProxyInventoryFacade.executeCmHandleIdSearch(cmHandleQueryApiParameters); return ResponseEntity.ok(List.copyOf(cmHandleIds)); } @@ -299,7 +297,8 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { */ @Override public ResponseEntity retrieveCmHandleDetailsById(final String cmHandleId) { - final NcmpServiceCmHandle ncmpServiceCmHandle = networkCmProxyDataService.getNcmpServiceCmHandle(cmHandleId); + final NcmpServiceCmHandle ncmpServiceCmHandle + = networkCmProxyInventoryFacade.getNcmpServiceCmHandle(cmHandleId); final RestOutputCmHandle restOutputCmHandle = toRestOutputCmHandle(ncmpServiceCmHandle); return ResponseEntity.ok(restOutputCmHandle); } @@ -314,7 +313,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { public ResponseEntity getCmHandlePublicPropertiesByCmHandleId( final String cmHandleId) { final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties(); - cmHandlePublicProperties.add(networkCmProxyDataService.getCmHandlePublicProperties(cmHandleId)); + cmHandlePublicProperties.add(networkCmProxyInventoryFacade.getCmHandlePublicProperties(cmHandleId)); final RestOutputCmHandlePublicProperties restOutputCmHandlePublicProperties = new RestOutputCmHandlePublicProperties(); restOutputCmHandlePublicProperties.setPublicCmHandleProperties(cmHandlePublicProperties); @@ -330,7 +329,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { @Override public ResponseEntity getCmHandleStateByCmHandleId( final String cmHandleId) { - final CompositeState cmHandleState = networkCmProxyDataService.getCmHandleCompositeState(cmHandleId); + final CompositeState cmHandleState = networkCmProxyInventoryFacade.getCmHandleCompositeState(cmHandleId); final RestOutputCmHandleCompositeState restOutputCmHandleCompositeState = new RestOutputCmHandleCompositeState(); restOutputCmHandleCompositeState.setState( @@ -353,9 +352,9 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final Collection moduleDefinitions; if (StringUtils.hasText(moduleName)) { moduleDefinitions = - networkCmProxyDataService.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, revision); + networkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, revision); } else { - moduleDefinitions = networkCmProxyDataService.getModuleDefinitionsByCmHandleId(cmHandleId); + moduleDefinitions = networkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleId(cmHandleId); if (StringUtils.hasText(revision)) { log.warn("Ignoring revision filter as no module name is provided"); } @@ -375,7 +374,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { */ public ResponseEntity> getModuleReferencesByCmHandle(final String cmHandle) { final List restModuleReferences = - networkCmProxyDataService.getYangResourcesModuleReferences(cmHandle).stream() + networkCmProxyInventoryFacade.getYangResourcesModuleReferences(cmHandle).stream() .map(ncmpRestInputMapper::toRestModuleReference) .collect(Collectors.toList()); return new ResponseEntity<>(restModuleReferences, HttpStatus.OK); @@ -391,7 +390,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { @Override public ResponseEntity setDataSyncEnabledFlagForCmHandle(final String cmHandleId, final Boolean dataSyncEnabledFlag) { - networkCmProxyDataService.setDataSyncEnabled(cmHandleId, dataSyncEnabledFlag); + networkCmProxyInventoryFacade.setDataSyncEnabled(cmHandleId, dataSyncEnabledFlag); return new ResponseEntity<>(HttpStatus.OK); } @@ -422,11 +421,5 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { } } - private NcmpDatastoreRequestHandler getNcmpDatastoreRequestHandler(final String datastoreName) { - if (OPERATIONAL.equals(DatastoreType.fromDatastoreName(datastoreName))) { - return ncmpCachedResourceRequestHandler; - } - return ncmpPassthroughResourceRequestHandler; - } } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java index 5467eeffca..5185b47bb5 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java @@ -27,7 +27,7 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; +import org.onap.cps.ncmp.api.impl.NetworkCmProxyInventoryFacade; import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status; @@ -47,7 +47,7 @@ import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor public class NetworkCmProxyInventoryController implements NetworkCmProxyInventoryApi { - private final NetworkCmProxyDataService networkCmProxyDataService; + private final NetworkCmProxyInventoryFacade networkCmProxyInventoryFacade; private final NcmpRestInputMapper ncmpRestInputMapper; @Override @@ -55,8 +55,8 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = ncmpRestInputMapper .toCmHandleQueryServiceParameters(cmHandleQueryParameters); - final Collection cmHandleIds = networkCmProxyDataService - .executeCmHandleIdSearchForInventory(cmHandleQueryServiceParameters); + final Collection cmHandleIds = networkCmProxyInventoryFacade + .executeParameterizedCmHandleIdSearch(cmHandleQueryServiceParameters); return ResponseEntity.ok(List.copyOf(cmHandleIds)); } @@ -69,7 +69,7 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor @Override public ResponseEntity> getAllCmHandleIdsForRegisteredDmi(final String dmiPluginIdentifier) { final Collection cmHandleIds = - networkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifier); + networkCmProxyInventoryFacade.getAllCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifier); return ResponseEntity.ok(List.copyOf(cmHandleIds)); } @@ -84,7 +84,7 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor public ResponseEntity updateDmiPluginRegistration( final @Valid RestDmiPluginRegistration restDmiPluginRegistration) { final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = - networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + networkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule( ncmpRestInputMapper.toDmiPluginRegistration(restDmiPluginRegistration)); final DmiPluginRegistrationErrorResponse failedRegistrationErrorResponse = getFailureRegistrationResponse(dmiPluginRegistrationResponse); diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy index 34b9dbe950..a498e25bf6 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy @@ -32,10 +32,8 @@ import groovy.json.JsonSlurper import org.mapstruct.factory.Mappers import org.onap.cps.TestUtils import org.onap.cps.events.EventsPublisher -import org.onap.cps.ncmp.api.NetworkCmProxyDataService -import org.onap.cps.ncmp.api.NetworkCmProxyQueryService -import org.onap.cps.ncmp.api.impl.NcmpCachedResourceRequestHandler -import org.onap.cps.ncmp.api.impl.NcmpPassthroughResourceRequestHandler +import org.onap.cps.ncmp.api.impl.NetworkCmProxyFacade +import org.onap.cps.ncmp.api.impl.NetworkCmProxyInventoryFacade import org.onap.cps.ncmp.api.impl.inventory.CmHandleState import org.onap.cps.ncmp.api.impl.inventory.CompositeState import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState @@ -48,7 +46,6 @@ import org.onap.cps.ncmp.rest.mapper.DataOperationRequestMapper import org.onap.cps.ncmp.rest.model.DataOperationDefinition import org.onap.cps.ncmp.rest.model.DataOperationRequest import org.onap.cps.ncmp.rest.util.DeprecationHelper -import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.ModuleDefinition import org.onap.cps.spi.model.ModuleReference import org.onap.cps.utils.JsonObjectMapper @@ -71,15 +68,12 @@ import java.time.format.DateTimeFormatter import static org.onap.cps.ncmp.api.impl.inventory.CompositeState.DataStores import static org.onap.cps.ncmp.api.impl.inventory.CompositeState.Operational -import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE import static org.onap.cps.ncmp.api.impl.operations.OperationType.DELETE import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE -import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS -import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch @@ -93,10 +87,10 @@ class NetworkCmProxyControllerSpec extends Specification { MockMvc mvc @SpringBean - NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock() + NetworkCmProxyFacade mockNetworkCmProxyFacade = Mock() @SpringBean - NetworkCmProxyQueryService mockNetworkCmProxyQueryService = Mock() + NetworkCmProxyInventoryFacade mockNetworkCmProxyInventoryFacade = Mock() @SpringBean ObjectMapper objectMapper = new ObjectMapper() @@ -119,12 +113,6 @@ class NetworkCmProxyControllerSpec extends Specification { @SpringBean DeprecationHelper stubbedDeprecationHelper = Stub() - @SpringBean - NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler = new NcmpCachedResourceRequestHandler(mockNetworkCmProxyDataService, mockNetworkCmProxyQueryService) - - @SpringBean - NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler = new NcmpPassthroughResourceRequestHandler(mockNetworkCmProxyDataService) - @Value('${rest.api.ncmp-base-path}/v1') def ncmpBasePathV1 @@ -134,17 +122,13 @@ class NetworkCmProxyControllerSpec extends Specification { @Shared def NO_TOPIC = null + def NO_OPTIONS = null def NO_REQUEST_ID = null def NO_AUTH_HEADER = null - def TIMEOUT_FOR_TEST = 1234 def logger = Spy(ListAppender) def setup() { - ncmpCachedResourceRequestHandler.notificationFeatureEnabled = true - ncmpCachedResourceRequestHandler.timeOutInMilliSeconds = TIMEOUT_FOR_TEST - ncmpPassthroughResourceRequestHandler.notificationFeatureEnabled = true - ncmpPassthroughResourceRequestHandler.timeOutInMilliSeconds = TIMEOUT_FOR_TEST setupLogger() } @@ -160,7 +144,7 @@ class NetworkCmProxyControllerSpec extends Specification { when: 'get data resource request is performed' def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response then: 'the NCMP data service is called with correct parameters' - 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(expectedCmResourceAddress, '(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >> Mono.just(new ResponseEntity(HttpStatus.OK)) + 1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(expectedCmResourceAddress, '(a=1,b=2)', NO_TOPIC, false, NO_AUTH_HEADER) >> Mono.just(new ResponseEntity(HttpStatus.OK)) and: 'response status is Ok' assert response.status == HttpStatus.OK.value() } @@ -173,17 +157,16 @@ class NetworkCmProxyControllerSpec extends Specification { when: 'get data resource request is performed' def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response then: 'the NCMP data service is called with correct parameters' - 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(expectedCmResourceAddress, expectedIncludeDescendants) + 1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(expectedCmResourceAddress, NO_OPTIONS, NO_TOPIC, expectedIncludeDescendants, NO_AUTH_HEADER) and: 'response status is OK' assert response.status == HttpStatus.OK.value() where: 'the following parameters are used' scenario | additionalUrlParam || expectedIncludeDescendants - 'no additional param' | '' || OMIT_DESCENDANTS - 'include descendants true' | '&include-descendants=true' || INCLUDE_ALL_DESCENDANTS - 'include descendants TRUE' | '&include-descendants=true' || INCLUDE_ALL_DESCENDANTS - 'include descendants false' | '&include-descendants=false' || OMIT_DESCENDANTS - 'include descendants FALSE' | '&include-descendants=FALSE' || OMIT_DESCENDANTS - 'options (ignored)' | '&options=(a-=1)' || OMIT_DESCENDANTS + 'no additional param' | '' || false + 'include descendants true' | '&include-descendants=true' || true + 'include descendants TRUE' | '&include-descendants=true' || true + 'include descendants false' | '&include-descendants=false' || false + 'include descendants FALSE' | '&include-descendants=FALSE' || false } def 'Execute (async) data operation to read data from dmi service.'() { @@ -194,45 +177,19 @@ class NetworkCmProxyControllerSpec extends Specification { def response = mvc.perform(post(getUrl).contentType(MediaType.APPLICATION_JSON).content(dataOperationRequestJsonData)).andReturn().response then: 'response status is Ok' assert response.status == HttpStatus.OK.value() - and: 'async request id is generated' - assert response.contentAsString.contains('requestId') then: 'the request for (async) data operation invoked once' - 1 * mockNetworkCmProxyDataService.executeDataOperationForCmHandles('my-topic-name', _, _, null) + 1 * mockNetworkCmProxyFacade.executeDataOperationForCmHandles('my-topic-name', _, NO_AUTH_HEADER) where: 'the following data stores are used' datastore << [PASSTHROUGH_RUNNING, PASSTHROUGH_OPERATIONAL] } - def 'Execute (async) data operation with some validation error.'() { - given: 'data operation url' - def getUrl = "$ncmpBasePathV1/data?topic=my-topic-name" - def dataOperationRequestJsonData = jsonObjectMapper.asJsonString(getDataOperationRequest('read', 'invalid datastore')) - when: 'post data resource request is performed' - def response = mvc.perform(post(getUrl).contentType(MediaType.APPLICATION_JSON).content(dataOperationRequestJsonData)).andReturn().response - then: 'response status is BAD_REQUEST' - assert response.status == HttpStatus.BAD_REQUEST.value() - } - - def 'Get data operation resource data when notification feature is disabled for datastore: #datastore.'() { - given: 'data operation url' - def getUrl = "$ncmpBasePathV1/data?topic=my-topic-name" - def dataOperationRequestJsonData = jsonObjectMapper.asJsonString(getDataOperationRequest("read", PASSTHROUGH_RUNNING.datastoreName)) - ncmpPassthroughResourceRequestHandler.notificationFeatureEnabled = false - when: 'post data resource request is performed' - def response = mvc.perform(post(getUrl).contentType(MediaType.APPLICATION_JSON).content(dataOperationRequestJsonData) - ).andReturn().response - then: 'response status is Ok' - assert response.status == HttpStatus.OK.value() - and: 'async request id is unavailable' - assert response.contentAsString == '{"status":"Asynchronous request is unavailable as notification feature is currently disabled."}' - } - def 'Query Resource Data from operational.'() { given: 'the query resource data url' - def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational/query?cps-path=/cps/path" + def getUrl = "$ncmpBasePathV1/ch/ch-1/data/ds/ncmp-datastore:operational/query?cps-path=/cps/path" when: 'the query data resource request is performed' def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response then: 'the NCMP query service is called with queryResourceDataOperationalForCmHandle' - 1 * mockNetworkCmProxyQueryService.queryResourceDataOperational('testCmHandle','/cps/path',FetchDescendantsOption.OMIT_DESCENDANTS) + 1 * mockNetworkCmProxyFacade.queryResourceDataForCmHandle('ch-1','/cps/path', false) and: 'response status is Ok' assert response.status == HttpStatus.OK.value() } @@ -250,11 +207,11 @@ class NetworkCmProxyControllerSpec extends Specification { def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() { given: 'resource data url' - def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=$resourceIdentifier&options=(a=1,b=2)" + def getUrl = "$ncmpBasePathV1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=$resourceIdentifier&options=(a=1)" and: 'ncmp service returns json object' - def expectedCmResourceAddress = new CmResourceAddress(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', resourceIdentifier) - 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(expectedCmResourceAddress, '(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) - >> Mono.just(new ResponseEntity('{valid-json}', HttpStatus.OK)) + def expectedCmResourceAddress = new CmResourceAddress(PASSTHROUGH_RUNNING.datastoreName, 'ch-1', resourceIdentifier) + 1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(expectedCmResourceAddress, '(a=1)', NO_TOPIC, false, NO_AUTH_HEADER) + >> new ResponseEntity('{valid-json}', HttpStatus.OK) when: 'get data resource request is performed' def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response then: 'response status is Ok' @@ -278,7 +235,7 @@ class NetworkCmProxyControllerSpec extends Specification { when: 'update data resource request is performed' def response = mvc.perform(put(updateUrl).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)).andReturn().response then: 'ncmp service method to update resource is called' - 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle','parent/child', UPDATE, requestBody, 'application/json;charset=UTF-8', NO_AUTH_HEADER) + 1 * mockNetworkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle('testCmHandle','parent/child', UPDATE, requestBody, 'application/json;charset=UTF-8', NO_AUTH_HEADER) and: 'the response status is OK' assert response.status == HttpStatus.OK.value() } @@ -289,7 +246,7 @@ class NetworkCmProxyControllerSpec extends Specification { when: 'create resource request is performed' def response = mvc.perform(post(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)).andReturn().response then: 'ncmp service method to create resource called' - 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', 'parent/child', CREATE, requestBody, 'application/json;charset=UTF-8', NO_AUTH_HEADER) + 1 * mockNetworkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', 'parent/child', CREATE, requestBody, 'application/json;charset=UTF-8', NO_AUTH_HEADER) and: 'resource is created' assert response.status == HttpStatus.CREATED.value() } @@ -300,7 +257,7 @@ class NetworkCmProxyControllerSpec extends Specification { when: 'get module resource request is performed' def response = mvc.perform(get(getUrl)).andReturn().response then: 'ncmp service method to get yang resource module references is called' - mockNetworkCmProxyDataService.getYangResourcesModuleReferences('some-cmhandle') >> [new ModuleReference(moduleName: 'some-name1', revision: '2021-10-03')] + mockNetworkCmProxyInventoryFacade.getYangResourcesModuleReferences('some-cmhandle') >> [new ModuleReference(moduleName: 'some-name1', revision: '2021-10-03')] and: 'response contains an array with the module name and revision' response.getContentAsString() == '[{"moduleName":"some-name1","revision":"2021-10-03"}]' and: 'response returns an OK http code' @@ -321,7 +278,7 @@ class NetworkCmProxyControllerSpec extends Specification { cmHandle2.alternateId = 'someAlternateId' cmHandle2.moduleSetTag = 'someModuleSetTag' cmHandle2.dataProducerIdentifier = 'someDataProducerIdentifier' - mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandle1, cmHandle2] + mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> [cmHandle1, cmHandle2] and: 'map for trust level per cmHandle has value for only one cm handle' trustLevelPerCmHandle.put('ch-1', TrustLevel.NONE) when: 'the searches api is invoked' @@ -342,7 +299,7 @@ class NetworkCmProxyControllerSpec extends Specification { def compositeState = compositeStateTestObject() def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState) and: 'the service method is invoked with the cm handle id' - 1 * mockNetworkCmProxyDataService.getNcmpServiceCmHandle('some-cm-handle') >> ncmpServiceCmHandle + 1 * mockNetworkCmProxyInventoryFacade.getNcmpServiceCmHandle('some-cm-handle') >> ncmpServiceCmHandle and: 'map for trust level per cmHandle has values' trustLevelPerCmHandle.get('some-cm-handle') >> { TrustLevel.COMPLETE } when: 'the cm handle details api is invoked' @@ -364,7 +321,7 @@ class NetworkCmProxyControllerSpec extends Specification { and: 'some cm handle public properties' def publicProperties = ['public prop': 'some public property'] and: 'the service method is invoked with the cm handle id returning the cm handle public properties' - 1 * mockNetworkCmProxyDataService.getCmHandlePublicProperties('some-cm-handle') >> publicProperties + 1 * mockNetworkCmProxyInventoryFacade.getCmHandlePublicProperties('some-cm-handle') >> publicProperties when: 'the cm handle properties api is invoked' def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response then: 'the correct response is returned' @@ -379,7 +336,7 @@ class NetworkCmProxyControllerSpec extends Specification { and: 'some cm handle composite state' def compositeState = compositeStateTestObject() and: 'the service method is invoked with the cm handle id returning the cm handle composite state' - 1 * mockNetworkCmProxyDataService.getCmHandleCompositeState('some-cm-handle') >> compositeState + 1 * mockNetworkCmProxyInventoryFacade.getCmHandleCompositeState('some-cm-handle') >> compositeState when: 'the cm handle state api is invoked' def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response then: 'the correct response is returned' @@ -399,7 +356,7 @@ class NetworkCmProxyControllerSpec extends Specification { def cmHandel2 = new NcmpServiceCmHandle() cmHandel2.cmHandleId = 'ch-2' cmHandel2.publicProperties = [color: 'green'] - mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandel1, cmHandel2] + mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> [cmHandel1, cmHandel2] and: 'map for trust level per cmHandle has values' trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE) trustLevelPerCmHandle.put('ch-2', TrustLevel.NONE) @@ -413,7 +370,7 @@ class NetworkCmProxyControllerSpec extends Specification { given: 'an endpoint and json data' def searchesEndpoint = "$ncmpBasePathV1/ch/id-searches" and: 'the service method is invoked with module names and returns cm handle ids' - 1 * mockNetworkCmProxyDataService.executeCmHandleIdSearch(_) >> ['ch-1', 'ch-2'] + 1 * mockNetworkCmProxyInventoryFacade.executeCmHandleIdSearch(_) >> ['ch-1', 'ch-2'] when: 'the searches api is invoked' def response = mvc.perform(post(searchesEndpoint).contentType(MediaType.APPLICATION_JSON).content('{}')).andReturn().response then: 'cm handle ids are returned' @@ -435,7 +392,7 @@ class NetworkCmProxyControllerSpec extends Specification { when: 'patch data resource request is performed' def response = mvc.perform(patch(url).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).content(requestBody)).andReturn().response then: 'ncmp service method to update resource is called' - 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', 'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8', NO_AUTH_HEADER) + 1 * mockNetworkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', 'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8', NO_AUTH_HEADER) and: 'the response status is OK' assert response.status == HttpStatus.OK.value() } @@ -446,31 +403,16 @@ class NetworkCmProxyControllerSpec extends Specification { when: 'delete data resource request is performed' def response = mvc.perform(delete(url).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andReturn().response then: 'the ncmp service method to delete resource is called (with null as body)' - 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', 'parent/child', DELETE, null, 'application/json;charset=UTF-8', NO_AUTH_HEADER) + 1 * mockNetworkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', 'parent/child', DELETE, null, 'application/json;charset=UTF-8', NO_AUTH_HEADER) and: 'the response is No Content' assert response.status == HttpStatus.NO_CONTENT.value() } - def 'Get resource data from DMI with valid topic i.e. async request for #scenario'() { - given: 'resource data url' - def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=my-topic-name" - and: 'the NCMP data service is called with correct parameters' - 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(_, '(a=1,b=2)', 'my-topic-name', _, NO_AUTH_HEADER) >> Mono.just(new ResponseEntity(HttpStatus.OK)) - when: 'get data resource request is performed' - def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON_VALUE)).andReturn().response - then: 'async request id is generated' - assert response.contentAsString.contains("requestId") - where: 'the following parameters are used' - scenario | datastoreInUrl - ':passthrough-operational' | 'passthrough-operational' - ':passthrough-running' | 'passthrough-running' - } - def 'Getting module definitions for a module'() { when: 'get module definition request is performed with module name' def response = mvc.perform(get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions?module-name=sampleModuleName")).andReturn().response then: 'ncmp service method is invoked with correct parameters' - mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleAndModule('some-cmhandle', 'sampleModuleName', _) + mockNetworkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleAndModule('some-cmhandle', 'sampleModuleName', _) >> [new ModuleDefinition('sampleModuleName', '2021-10-03','module sampleModuleName{ sample module content }')] and: 'response contains an array with the module name, revision and content' response.getContentAsString() == '[{"moduleName":"sampleModuleName","revision":"2021-10-03","content":"module sampleModuleName{ sample module content }"}]' @@ -484,9 +426,9 @@ class NetworkCmProxyControllerSpec extends Specification { get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions?module-name=" + moduleName + "&revision=" + revision)) .andReturn().response then: 'ncmp service method to get definitions by cm handle is invoked when needed' - numberOfCallsToByCmHandleId * mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleId('some-cmhandle') >> [] + numberOfCallsToByCmHandleId * mockNetworkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleId('some-cmhandle') >> [] and: 'ncmp service method to get definitions by module is invoked when needed' - numberOfCallsToByModule * mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleAndModule('some-cmhandle', moduleName, revision) >> [] + numberOfCallsToByModule * mockNetworkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleAndModule('some-cmhandle', moduleName, revision) >> [] and: 'response returns an OK http code' response.status == HttpStatus.OK.value() and: 'the correct message is logged when needed' @@ -509,7 +451,7 @@ class NetworkCmProxyControllerSpec extends Specification { put("$ncmpBasePathV1/ch/some-cm-handle-id/data-sync?dataSyncEnabled=" + dataSyncEnabledFlag)) .andReturn().response then: 'method to set data sync enabled is called' - 1 * mockNetworkCmProxyDataService.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag) + 1 * mockNetworkCmProxyInventoryFacade.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag) and: 'the response returns an OK http code' response.status == HttpStatus.OK.value() where: 'the following parameters are used' @@ -518,23 +460,6 @@ class NetworkCmProxyControllerSpec extends Specification { 'disabled' | false } - def 'Get Resource Data from operational with or without descendants'() { - given: 'resource data url with descendants #enabled' - def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational?resourceIdentifier=parent/child&include-descendants=${booleanValue}" - and: 'the expected target' - def expectedCmResourceAddress = new CmResourceAddress(OPERATIONAL.datastoreName, 'testCmHandle', 'parent/child') - when: 'get data resource request is performed' - def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response - then: 'the NCMP data service is called with getResourceDataOperational with #descendantsOption' - 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(expectedCmResourceAddress, descendantsOption) - and: 'response status is Ok' - assert response.status == HttpStatus.OK.value() - where: 'the following parameters are used' - booleanValue | descendantsOption - false | OMIT_DESCENDANTS - true | INCLUDE_ALL_DESCENDANTS - } - def 'Attempt execute #operation rest operation on resource data with #scenario'() { given: 'resource data url' def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/${datastoreInUrl}?resourceIdentifier=parent/child" diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy index 7b850a7fff..60d2dc5729 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy @@ -23,7 +23,9 @@ package org.onap.cps.ncmp.rest.controller import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.TestUtils -import org.onap.cps.ncmp.api.NetworkCmProxyDataService +import org.onap.cps.ncmp.api.impl.NetworkCmProxyFacade +import org.onap.cps.ncmp.api.impl.NetworkCmProxyInventoryFacade +import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse import org.onap.cps.ncmp.api.models.DmiPluginRegistration import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse @@ -31,7 +33,6 @@ import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters import org.onap.cps.ncmp.rest.model.CmHandlerRegistrationErrorResponse import org.onap.cps.ncmp.rest.model.DmiPluginRegistrationErrorResponse import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters import org.onap.cps.utils.JsonObjectMapper import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired @@ -54,7 +55,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { MockMvc mvc @SpringBean - NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock() + NetworkCmProxyInventoryFacade mockNetworkCmProxyInventoryFacade = Mock() @SpringBean NcmpRestInputMapper ncmpRestInputMapper = Mock() @@ -83,7 +84,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { .content(jsonData) ).andReturn().response then: 'the converted object is forwarded to the registration service' - 1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(mockDmiPluginRegistration) >> new DmiPluginRegistrationResponse() + 1 * mockNetworkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(mockDmiPluginRegistration) >> new DmiPluginRegistrationResponse() and: 'response status is no content' response.status == HttpStatus.OK.value() where: 'the following registration json is used' @@ -112,7 +113,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { and: 'the mapper service returns a converted object' ncmpRestInputMapper.toCmHandleQueryServiceParameters(_) >> cmHandleQueryServiceParameters and: 'the service returns the desired results' - mockNetworkCmProxyDataService.executeCmHandleIdSearchForInventory(cmHandleQueryServiceParameters) >> serviceMockResponse + mockNetworkCmProxyInventoryFacade.executeParameterizedCmHandleIdSearch(cmHandleQueryServiceParameters) >> serviceMockResponse when: 'post request is performed & search is called with the given request parameters' def response = mvc.perform( post("$ncmpBasePathV1/ch/searches") @@ -135,7 +136,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { and: 'the mapper service returns a converted object' ncmpRestInputMapper.toCmHandleQueryServiceParameters(_) >> cmHandleQueryServiceParameters and: 'the service returns the desired results' - mockNetworkCmProxyDataService.executeCmHandleIdSearchForInventory(cmHandleQueryServiceParameters) >> serviceMockResponse + mockNetworkCmProxyInventoryFacade.executeParameterizedCmHandleIdSearch(cmHandleQueryServiceParameters) >> serviceMockResponse when: 'post request is performed & search is called with the given request parameters' def response = mvc.perform( post("$ncmpBasePathV1/ch/searches") @@ -156,7 +157,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { given: 'the mapper service returns a converted object' ncmpRestInputMapper.toCmHandleQueryServiceParameters(_) >> cmHandleQueryServiceParameters and: 'the service returns the desired results' - mockNetworkCmProxyDataService.executeCmHandleIdSearchForInventory(cmHandleQueryServiceParameters) >> [] + mockNetworkCmProxyInventoryFacade.executeParameterizedCmHandleIdSearch(cmHandleQueryServiceParameters) >> [] when: 'post request is performed & search is called with the given request parameters' def response = mvc.perform( post("$ncmpBasePathV1/ch/searches") @@ -180,7 +181,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { updatedCmHandles: [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-2')], removedCmHandles: [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-3')] ) - mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(*_) >> dmiRegistrationResponse + mockNetworkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(*_) >> dmiRegistrationResponse when: 'registration endpoint is invoked' def response = mvc.perform( post("$ncmpBasePathV1/ch") @@ -204,7 +205,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { removedCmHandles: [removeCmHandleResponse], upgradedCmHandles: [upgradeCmHandleResponse] ) - mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(*_) >> dmiRegistrationResponse + mockNetworkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(*_) >> dmiRegistrationResponse when: 'registration endpoint is invoked' def response = mvc.perform( post("$ncmpBasePathV1/ch") @@ -237,7 +238,7 @@ class NetworkCmProxyInventoryControllerSpec extends Specification { given: 'an endpoint for returning cm handle IDs for a registered dmi plugin' def getUrl = "$ncmpBasePathV1/ch/cmHandles?dmi-plugin-identifier=some-dmi-plugin-identifier" and: 'a collection of cm handle IDs are returned' - 1 * mockNetworkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') + 1 * mockNetworkCmProxyInventoryFacade.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-id-1','cm-handle-id-2'] when: 'the endpoint is invoked' def response = mvc.perform( diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy index af8a8ea12a..20e8abeaa2 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy @@ -24,9 +24,10 @@ package org.onap.cps.ncmp.rest.exceptions import groovy.json.JsonSlurper import org.mapstruct.factory.Mappers import org.onap.cps.TestUtils -import org.onap.cps.ncmp.api.NetworkCmProxyDataService import org.onap.cps.ncmp.api.impl.NcmpCachedResourceRequestHandler import org.onap.cps.ncmp.api.impl.NcmpPassthroughResourceRequestHandler +import org.onap.cps.ncmp.api.impl.NetworkCmProxyFacade +import org.onap.cps.ncmp.api.impl.NetworkCmProxyInventoryFacade import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException import org.onap.cps.ncmp.api.impl.exception.DmiRequestException import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException @@ -68,7 +69,10 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { MockMvc mvc @SpringBean - NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock() + NetworkCmProxyFacade mockNetworkCmProxyFacade = Mock() + + @SpringBean + NetworkCmProxyInventoryFacade mockNetworkCmProxyInventoryFacade = Mock() @SpringBean JsonObjectMapper stubbedJsonObjectMapper = Stub() @@ -153,9 +157,9 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { def setupTestException(exception, apiType) { if (NCMP == apiType) { - mockNetworkCmProxyDataService.getYangResourcesModuleReferences(*_) >> { throw exception } + mockNetworkCmProxyInventoryFacade.getYangResourcesModuleReferences(*_) >> { throw exception } } - mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(*_) >> { throw exception } + mockNetworkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(*_) >> { throw exception } } def performTestRequest(apiType) { @@ -174,8 +178,5 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { assert expectedErrorDetails == null || content['details'].toString().contains(expectedErrorDetails) } - enum ApiType { - NCMP, - NCMPINVENTORY; - } + enum ApiType { NCMP, NCMPINVENTORY } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java deleted file mode 100644 index 06522f80cf..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api; - -import java.util.Collection; -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; - -public interface NetworkCmProxyCmHandleQueryService { - /** - * Query and return cm handle ids that match the given query parameters. - * Supported query types: - * public properties - * modules - * cps-path - * - * @param cmHandleQueryServiceParameters the cm handle query parameters - * @return collection of cm handle ids - */ - Collection queryCmHandleIds(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); - - /** - * Query and return cm handle ids that match the given query parameters. - * Supported query types: - * public properties - * private (additional) properties - * dmi-names - * The inventory interface also allows conditions on private (additional) properties and dmi names - * - * @param cmHandleQueryServiceParameters the cm handle query parameters - * @return collection of cm handle ids - */ - Collection queryCmHandleIdsForInventory(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); - - /** - * Query and return cm handle objects that match the given query parameters. - * Supported query types: - * public properties - * modules - * cps-path - * - * @param cmHandleQueryServiceParameters the cm handle query parameters - * @return collection of cm handles - */ - Collection queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); - - /** - * Query and return all cm handle objects. - * - * @return collection of cm handles - */ - Collection getAllCmHandles(); -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java deleted file mode 100644 index 73c8d96096..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 highstreet technologies GmbH - * Modifications Copyright (C) 2021-2024 Nordix Foundation - * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2022 Bell Canada - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api; - -import java.util.Collection; -import java.util.Map; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.operations.OperationType; -import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.models.CmResourceAddress; -import org.onap.cps.ncmp.api.models.DataOperationRequest; -import org.onap.cps.ncmp.api.models.DmiPluginRegistration; -import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -import org.onap.cps.spi.FetchDescendantsOption; -import org.onap.cps.spi.model.ModuleDefinition; -import org.onap.cps.spi.model.ModuleReference; -import reactor.core.publisher.Mono; - -/* - * Datastore interface for handling CPS data. - */ -public interface NetworkCmProxyDataService { - - /** - * Registration of New CM Handles. - * - * @param dmiPluginRegistration Dmi Plugin Registration - * @return dmiPluginRegistrationResponse - */ - DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration); - - /** - * Fetches resource data for a given data store using DMI (Data Management Interface). - * This method retrieves data based on the provided CmResourceAddress and additional query parameters. - * It supports asynchronous processing and handles authorization if required. - * - * @param cmResourceAddress The target data store, including the CM handle and resource identifier. - * This parameter must not be null. - * @param optionsParamInQuery Additional query parameters that may influence the data retrieval process, - * such as filters or limits. This parameter can be null. - * @param topicParamInQuery The topic name for triggering asynchronous responses. If specified, - * the response will be sent to this topic. This parameter can be null. - * @param requestId A unique identifier for the request, used for tracking and correlating - * asynchronous operations. This parameter must not be null. - * @param authorization The contents of the Authorization header. This parameter can be null - * if authorization is not required. - * @return {@code Mono} A reactive Mono that emits the resource data on successful retrieval - * or an error signal if the operation fails. The Mono represents a single asynchronous - * computation result. - */ - Mono getResourceDataForCmHandle(CmResourceAddress cmResourceAddress, - String optionsParamInQuery, - String topicParamInQuery, - String requestId, - String authorization); - - /** - * Get resource data for operational. - * - * @param cmResourceAddress target datastore, cm handle and resource identifier - * @Link FetchDescendantsOption fetch descendants option - * @return {@code Object} resource data - */ - Object getResourceDataForCmHandle(CmResourceAddress cmResourceAddress, - FetchDescendantsOption fetchDescendantsOption); - - /** - * Execute (async) data operation for group of cm handles using dmi. - * - * @param topicParamInQuery topic name for (triggering) async responses - * @param dataOperationRequest contains a list of operation definitions(multiple operations) - * @param requestId request ID - * @param authorization contents of Authorization header, or null if not present - */ - void executeDataOperationForCmHandles(String topicParamInQuery, - DataOperationRequest dataOperationRequest, - String requestId, - String authorization); - - - /** - * Write resource data for data store pass-through running using dmi for given cm-handle. - * - * @param cmHandleId cm handle identifier - * @param resourceIdentifier resource identifier - * @param operationType required operation type - * @param requestBody request body to create resource - * @param contentType content type in body - * @param authorization contents of Authorization header, or null if not present - * @return {@code Object} return data - */ - Object writeResourceDataPassThroughRunningForCmHandle(String cmHandleId, - String resourceIdentifier, - OperationType operationType, - String requestBody, - String contentType, - String authorization); - - /** - * Retrieve module references for the given cm handle. - * - * @param cmHandleId cm handle identifier - * @return a collection of modules names and revisions - */ - Collection getYangResourcesModuleReferences(String cmHandleId); - - /** - * Retrieve module definitions for the given cm handle. - * - * @param cmHandleId cm handle identifier - * @return a collection of module definition (moduleName, revision and yang resource content) - */ - Collection getModuleDefinitionsByCmHandleId(String cmHandleId); - - /** - * Get module definitions for the given parameters. - * - * @param cmHandleId cm-handle identifier - * @param moduleName module name - * @param moduleRevision the revision of the module - * @return list of module definitions (module name, revision, yang resource content) - */ - Collection getModuleDefinitionsByCmHandleAndModule(String cmHandleId, - String moduleName, - String moduleRevision); - - /** - * Query cm handle details by cm handle's name. - * - * @param cmHandleId cm handle identifier - * @return a collection of cm handle details. - */ - NcmpServiceCmHandle getNcmpServiceCmHandle(String cmHandleId); - - /** - * Get cm handle public properties by cm handle id. - * - * @param cmHandleId cm handle identifier - * @return a collection of cm handle public properties. - */ - Map getCmHandlePublicProperties(String cmHandleId); - - /** - * Get cm handle composite state by cm handle id. - * - * @param cmHandleId cm handle identifier - * @return a cm handle composite state - */ - CompositeState getCmHandleCompositeState(String cmHandleId); - - /** - * Query and return cm handles that match the given query parameters. - * - * @param cmHandleQueryApiParameters the cm handle query parameters - * @return collection of cm handles - */ - Collection executeCmHandleSearch(CmHandleQueryApiParameters cmHandleQueryApiParameters); - - /** - * Query and return cm handle ids that match the given query parameters. - * - * @param cmHandleQueryApiParameters the cm handle query parameters - * @return collection of cm handle ids - */ - Collection executeCmHandleIdSearch(CmHandleQueryApiParameters cmHandleQueryApiParameters); - - /** - * Set the data sync enabled flag, along with the data sync state to true or false based on the cm handle id. - * - * @param cmHandleId cm handle id - * @param dataSyncEnabled data sync enabled flag - */ - void setDataSyncEnabled(String cmHandleId, Boolean dataSyncEnabled); - - /** - * Get all cm handle IDs by DMI plugin identifier. - * - * @param dmiPluginIdentifier DMI plugin identifier - * @return collection of cm handle IDs - */ - Collection getAllCmHandleIdsByDmiPluginIdentifier(String dmiPluginIdentifier); - - /** - * Get all cm handle IDs by various search criteria. - * - * @param cmHandleQueryServiceParameters cm handle query parameters - * @return collection of cm handle IDs - */ - Collection executeCmHandleIdSearchForInventory(CmHandleQueryServiceParameters - cmHandleQueryServiceParameters); -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/ParameterizedCmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/ParameterizedCmHandleQueryService.java new file mode 100644 index 0000000000..a9ade24c72 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/ParameterizedCmHandleQueryService.java @@ -0,0 +1,72 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api; + +import java.util.Collection; +import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; + +public interface ParameterizedCmHandleQueryService { + /** + * Query and return cm handle ids that match the given query parameters. + * Supported query types: + * public properties + * modules + * cps-path + * + * @param cmHandleQueryServiceParameters the cm handle query parameters + * @return collection of cm handle ids + */ + Collection queryCmHandleIds(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); + + /** + * Query and return cm handle ids that match the given query parameters. + * Supported query types: + * public properties + * private (additional) properties + * dmi-names + * The inventory interface also allows conditions on private (additional) properties and dmi names + * + * @param cmHandleQueryServiceParameters the cm handle query parameters + * @return collection of cm handle ids + */ + Collection queryCmHandleIdsForInventory(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); + + /** + * Query and return cm handle objects that match the given query parameters. + * Supported query types: + * public properties + * modules + * cps-path + * + * @param cmHandleQueryServiceParameters the cm handle query parameters + * @return collection of cm handles + */ + Collection queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); + + /** + * Get all cm handle objects. + * Note: it is similar to all the queries above but simply no conditions and hence not 'parameterized' + * + * @return collection of cm handles + */ + Collection getAllCmHandles(); +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/CmHandleRegistrationService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/CmHandleRegistrationService.java new file mode 100644 index 0000000000..6f78e6cb34 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/CmHandleRegistrationService.java @@ -0,0 +1,404 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2021-2022 Bell Canada + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl; + +import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; + +import com.google.common.collect.Lists; +import com.hazelcast.map.IMap; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.onap.cps.api.CpsDataService; +import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; +import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; +import org.onap.cps.ncmp.api.impl.inventory.CompositeState; +import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder; +import org.onap.cps.ncmp.api.impl.inventory.CompositeStateUtils; +import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState; +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleOperationsUtils; +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager; +import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; +import org.onap.cps.ncmp.api.models.DmiPluginRegistration; +import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.spi.exceptions.AlreadyDefinedException; +import org.onap.cps.spi.exceptions.CpsException; +import org.onap.cps.spi.exceptions.DataNodeNotFoundException; +import org.onap.cps.spi.exceptions.DataValidationException; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class CmHandleRegistrationService { + + private static final int DELETE_BATCH_SIZE = 100; + + private final CmHandleRegistrationServicePropertyHandler cmHandleRegistrationServicePropertyHandler; + private final InventoryPersistence inventoryPersistence; + private final CpsDataService cpsDataService; + private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler; + private final IMap moduleSyncStartedOnCmHandles; + + @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN) + private final Map trustLevelPerDmiPlugin; + private final TrustLevelManager trustLevelManager; + + private final AlternateIdChecker alternateIdChecker; + + /** + * Registration of Created, Removed, Updated or Upgraded CM Handles. + * + * @param dmiPluginRegistration Dmi Plugin Registration details + * @return dmiPluginRegistrationResponse + */ + public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( + final DmiPluginRegistration dmiPluginRegistration) { + + dmiPluginRegistration.validateDmiPluginRegistration(); + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse(); + + setTrustLevelPerDmiPlugin(dmiPluginRegistration); + + processRemovedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); + + processCreatedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); + + processUpdatedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); + + processUpgradedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); + + return dmiPluginRegistrationResponse; + } + + /** + * Set the data sync enabled flag, along with the data sync state + * based on the data sync enabled boolean for the cm handle id provided. + * + * @param cmHandleId cm handle id + * @param dataSyncEnabledTargetValue data sync enabled flag + */ + public void setDataSyncEnabled(final String cmHandleId, final Boolean dataSyncEnabledTargetValue) { + final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); + if (dataSyncEnabledTargetValue.equals(compositeState.getDataSyncEnabled())) { + log.info("Data-Sync Enabled flag is already: {} ", dataSyncEnabledTargetValue); + return; + } + if (CmHandleState.READY.equals(compositeState.getCmHandleState())) { + final DataStoreSyncState dataStoreSyncState = compositeState.getDataStores() + .getOperationalDataStore().getDataStoreSyncState(); + if (Boolean.FALSE.equals(dataSyncEnabledTargetValue) + && DataStoreSyncState.SYNCHRONIZED.equals(dataStoreSyncState)) { + // TODO : This is hard-coded for onap dmi that need to be addressed + cpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, + "/netconf-state", OffsetDateTime.now()); + } + CompositeStateUtils.setDataSyncEnabledFlagWithDataSyncState(dataSyncEnabledTargetValue, compositeState); + inventoryPersistence.saveCmHandleState(cmHandleId, compositeState); + } else { + throw new CpsException("State mismatch exception.", "Cm-Handle not in READY state. Cm handle state is: " + + compositeState.getCmHandleState()); + } + } + + protected void processRemovedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + final List tobeRemovedCmHandleIds = dmiPluginRegistration.getRemovedCmHandles(); + final List cmHandleRegistrationResponses = + new ArrayList<>(tobeRemovedCmHandleIds.size()); + final Collection yangModelCmHandles = + inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandleIds); + updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING); + + final Set notDeletedCmHandles = new HashSet<>(); + for (final List tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandleIds, DELETE_BATCH_SIZE)) { + try { + batchDeleteCmHandlesFromDbAndModuleSyncMap(tobeRemovedCmHandleBatch); + tobeRemovedCmHandleBatch.forEach(cmHandleId -> + cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId))); + + } catch (final RuntimeException batchException) { + log.error("Unable to de-register cm-handle batch, retrying on each cm handle"); + for (final String cmHandleId : tobeRemovedCmHandleBatch) { + final CmHandleRegistrationResponse cmHandleRegistrationResponse = + deleteCmHandleAndGetCmHandleRegistrationResponse(cmHandleId); + cmHandleRegistrationResponses.add(cmHandleRegistrationResponse); + if (cmHandleRegistrationResponse.getStatus() != CmHandleRegistrationResponse.Status.SUCCESS) { + notDeletedCmHandles.add(cmHandleId); + } + } + } + } + yangModelCmHandles.removeIf(yangModelCmHandle -> notDeletedCmHandles.contains(yangModelCmHandle.getId())); + updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETED); + dmiPluginRegistrationResponse.setRemovedCmHandles(cmHandleRegistrationResponses); + } + + protected void processCreatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + final List ncmpServiceCmHandles = dmiPluginRegistration.getCreatedCmHandles(); + final List failedCmHandleRegistrationResponses = new ArrayList<>(); + + try { + final Collection rejectedCmHandleIds + = checkAlternateIds(ncmpServiceCmHandles, failedCmHandleRegistrationResponses); + + final Collection succeededCmHandleIds = persistCmHandlesWithState(dmiPluginRegistration, + dmiPluginRegistrationResponse, ncmpServiceCmHandles, rejectedCmHandleIds); + + processTrustLevels(ncmpServiceCmHandles, succeededCmHandleIds); + + } catch (final AlreadyDefinedException alreadyDefinedException) { + failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponsesFromXpaths( + alreadyDefinedException.getAlreadyDefinedObjectNames(), CM_HANDLE_ALREADY_EXIST)); + } catch (final Exception exception) { + final Collection cmHandleIds = + ncmpServiceCmHandles.stream().map(NcmpServiceCmHandle::getCmHandleId).collect(Collectors.toList()); + failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse + .createFailureResponses(cmHandleIds, exception)); + } + final List mergedCmHandleRegistrationResponses + = new ArrayList<>(failedCmHandleRegistrationResponses); + mergedCmHandleRegistrationResponses.addAll(dmiPluginRegistrationResponse.getCreatedCmHandles()); + + dmiPluginRegistrationResponse.setCreatedCmHandles(mergedCmHandleRegistrationResponses); + } + + protected void processUpdatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + dmiPluginRegistrationResponse.setUpdatedCmHandles(cmHandleRegistrationServicePropertyHandler + .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles())); + } + + protected void processUpgradedCmHandles( + final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + + final List cmHandleIds = dmiPluginRegistration.getUpgradedCmHandles().getCmHandles(); + final String upgradedModuleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag(); + final Map acceptedCmHandleStatePerCmHandle + = new HashMap<>(cmHandleIds.size()); + final List cmHandleUpgradeResponses = new ArrayList<>(cmHandleIds.size()); + + for (final String cmHandleId : cmHandleIds) { + try { + final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); + if (yangModelCmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY) { + if (moduleUpgradeCanBeSkipped(yangModelCmHandle, upgradedModuleSetTag)) { + cmHandleUpgradeResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)); + } else { + updateYangModelCmHandleForUpgrade(yangModelCmHandle, upgradedModuleSetTag); + acceptedCmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED); + } + } else { + cmHandleUpgradeResponses.add( + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_READY)); + } + } catch (final DataNodeNotFoundException dataNodeNotFoundException) { + log.error("Unable to find data node for cm handle id : {} , caused by : {}", + cmHandleId, dataNodeNotFoundException.getMessage()); + cmHandleUpgradeResponses.add( + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND)); + } catch (final DataValidationException dataValidationException) { + log.error("Unable to upgrade cm handle id: {}, caused by : {}", + cmHandleId, dataValidationException.getMessage()); + cmHandleUpgradeResponses.add( + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID)); + } + } + cmHandleUpgradeResponses.addAll(upgradeCmHandles(acceptedCmHandleStatePerCmHandle)); + dmiPluginRegistrationResponse.setUpgradedCmHandles(cmHandleUpgradeResponses); + } + + private void processTrustLevels(final Collection cmHandlesToBeCreated, + final Collection succeededCmHandleIds) { + final Map initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size()); + for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) { + if (succeededCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) { + initialTrustLevelPerCmHandleId.put(ncmpServiceCmHandle.getCmHandleId(), + ncmpServiceCmHandle.getRegistrationTrustLevel()); + } + } + trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId); + } + + private static boolean moduleUpgradeCanBeSkipped(final YangModelCmHandle yangModelCmHandle, + final String upgradedModuleSetTag) { + if (StringUtils.isBlank(upgradedModuleSetTag)) { + return false; + } + return yangModelCmHandle.getModuleSetTag().equals(upgradedModuleSetTag); + } + + private static void updateYangModelCmHandleForUpgrade(final YangModelCmHandle yangModelCmHandle, + final String upgradedModuleSetTag) { + final String lockReasonWithModuleSetTag = String.format(ModuleOperationsUtils.MODULE_SET_TAG_MESSAGE_FORMAT, + upgradedModuleSetTag); + yangModelCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.READY) + .withLockReason(MODULE_UPGRADE, lockReasonWithModuleSetTag).build()); + } + + private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) { + try { + deleteCmHandleFromDbAndModuleSyncMap(cmHandleId); + return CmHandleRegistrationResponse.createSuccessResponse(cmHandleId); + } catch (final DataNodeNotFoundException dataNodeNotFoundException) { + log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", + cmHandleId, dataNodeNotFoundException.getMessage()); + return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND); + } catch (final DataValidationException dataValidationException) { + log.error("Unable to de-register cm-handle id: {}, caused by: {}", + cmHandleId, dataValidationException.getMessage()); + return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID); + } catch (final Exception exception) { + log.error("Unable to de-register cm-handle id : {} , caused by : {}", cmHandleId, exception.getMessage()); + return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception); + } + } + + private void updateCmHandleStateBatch(final Collection yangModelCmHandles, + final CmHandleState cmHandleState) { + final Map cmHandleStatePerCmHandle = new HashMap<>(yangModelCmHandles.size()); + yangModelCmHandles.forEach(yangModelCmHandle -> cmHandleStatePerCmHandle.put(yangModelCmHandle, cmHandleState)); + lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); + } + + private void deleteCmHandleFromDbAndModuleSyncMap(final String cmHandleId) { + inventoryPersistence.deleteSchemaSetWithCascade(cmHandleId); + inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']"); + removeDeletedCmHandleFromModuleSyncMap(cmHandleId); + } + + private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection cmHandleIds) { + inventoryPersistence.deleteSchemaSetsWithCascade(cmHandleIds); + inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(cmHandleIds)); + cmHandleIds.forEach(this::removeDeletedCmHandleFromModuleSyncMap); + } + + private Collection mapCmHandleIdsToXpaths(final Collection cmHandles) { + return cmHandles.stream() + .map(cmHandleId -> NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']") + .collect(Collectors.toSet()); + } + + // CPS-1239 Robustness cleaning of in progress cache + private void removeDeletedCmHandleFromModuleSyncMap(final String cmHandleId) { + if (moduleSyncStartedOnCmHandles.remove(cmHandleId) != null) { + log.debug("{} removed from in progress map", cmHandleId); + } + } + + private List upgradeCmHandles(final Map + cmHandleStatePerCmHandle) { + final List cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle); + log.info("Moving cm handles : {} into locked (for upgrade) state.", cmHandleIds); + try { + lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); + return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds); + } catch (final Exception e) { + log.error("Unable to update cmHandleIds : {} , caused by : {}", cmHandleIds, e.getMessage()); + return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, e); + } + } + + private static List getCmHandleIds(final Map cmHandleStatePerCmHandle) { + return cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId).toList(); + } + + private void setTrustLevelPerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) { + if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) { + trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiPlugin(), TrustLevel.COMPLETE); + } else { + trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiDataPlugin(), TrustLevel.COMPLETE); + } + } + + private Collection checkAlternateIds( + final List cmHandlesToBeCreated, + final List cmHandleRegistrationResponses) { + final Collection rejectedCmHandleIds = alternateIdChecker + .getIdsOfCmHandlesWithRejectedAlternateId(cmHandlesToBeCreated, AlternateIdChecker.Operation.CREATE); + cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses( + rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED)); + return rejectedCmHandleIds; + } + + private List persistCmHandlesWithState(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse, + final List cmHandlesToBeCreated, + final Collection rejectedCmHandleIds) { + final List succeededCmHandleIds = new ArrayList<>(cmHandlesToBeCreated.size()); + final List yangModelCmHandlesToRegister = new ArrayList<>(cmHandlesToBeCreated.size()); + final List cmHandleRegistrationResponses = + new ArrayList<>(cmHandlesToBeCreated.size()); + for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) { + if (!rejectedCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) { + yangModelCmHandlesToRegister.add(getYangModelCmHandle(dmiPluginRegistration, ncmpServiceCmHandle)); + cmHandleRegistrationResponses.add( + CmHandleRegistrationResponse.createSuccessResponse(ncmpServiceCmHandle.getCmHandleId())); + succeededCmHandleIds.add(ncmpServiceCmHandle.getCmHandleId()); + } + } + lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandlesToRegister); + dmiPluginRegistrationResponse.setCreatedCmHandles(cmHandleRegistrationResponses); + return succeededCmHandleIds; + } + + private YangModelCmHandle getYangModelCmHandle(final DmiPluginRegistration dmiPluginRegistration, + final NcmpServiceCmHandle ncmpServiceCmHandle) { + return YangModelCmHandle.toYangModelCmHandle( + dmiPluginRegistration.getDmiPlugin(), + dmiPluginRegistration.getDmiDataPlugin(), + dmiPluginRegistration.getDmiModelPlugin(), + ncmpServiceCmHandle, + ncmpServiceCmHandle.getModuleSetTag(), + ncmpServiceCmHandle.getAlternateId(), + ncmpServiceCmHandle.getDataProducerIdentifier()); + } + + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServicePropertyHandler.java new file mode 100644 index 0000000000..1dac33f670 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServicePropertyHandler.java @@ -0,0 +1,246 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * Modifications Copyright (C) 2022 Bell Canada + * Modifications Copyright (C) 2024 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl; + +import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; +import static org.onap.cps.ncmp.api.impl.CmHandleRegistrationServicePropertyHandler.PropertyType.DMI_PROPERTY; +import static org.onap.cps.ncmp.api.impl.CmHandleRegistrationServicePropertyHandler.PropertyType.PUBLIC_PROPERTY; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; + +import com.google.common.collect.ImmutableMap; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.api.CpsDataService; +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker; +import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +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.ContentType; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +@Slf4j +@Service +@RequiredArgsConstructor +//Accepting the security hotspot as the string checked is generated from inside code and not user input. +@SuppressWarnings("squid:S5852") +public class CmHandleRegistrationServicePropertyHandler { + + private final InventoryPersistence inventoryPersistence; + private final CpsDataService cpsDataService; + private final JsonObjectMapper jsonObjectMapper; + private final AlternateIdChecker alternateIdChecker; + + /** + * Iterates over incoming updatedNcmpServiceCmHandles and update the dataNodes based on the updated attributes. + * The attributes which are not passed will remain as is. + * + * @param updatedNcmpServiceCmHandles collection of CmHandles + */ + public List updateCmHandleProperties( + final Collection updatedNcmpServiceCmHandles) { + final Collection rejectedCmHandleIds = alternateIdChecker + .getIdsOfCmHandlesWithRejectedAlternateId(updatedNcmpServiceCmHandles, AlternateIdChecker.Operation.UPDATE); + final List failureResponses = + CmHandleRegistrationResponse.createFailureResponses(rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED); + final List cmHandleRegistrationResponses = new ArrayList<>(failureResponses); + for (final NcmpServiceCmHandle updatedNcmpServiceCmHandle : updatedNcmpServiceCmHandles) { + final String cmHandleId = updatedNcmpServiceCmHandle.getCmHandleId(); + if (!rejectedCmHandleIds.contains(cmHandleId)) { + try { + final DataNode existingCmHandleDataNode = inventoryPersistence + .getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next(); + processUpdates(existingCmHandleDataNode, updatedNcmpServiceCmHandle); + cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)); + } catch (final DataNodeNotFoundException e) { + log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", cmHandleId, + e.getMessage()); + cmHandleRegistrationResponses.add( + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND)); + } catch (final DataValidationException e) { + log.error("Unable to update cm handle : {}, caused by : {}", cmHandleId, e.getMessage()); + cmHandleRegistrationResponses.add( + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID)); + } catch (final Exception exception) { + log.error("Unable to update cmHandle : {} , caused by : {}", cmHandleId, exception.getMessage()); + cmHandleRegistrationResponses.add( + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception)); + } + } + } + return cmHandleRegistrationResponses; + } + + private void processUpdates(final DataNode existingCmHandleDataNode, + final NcmpServiceCmHandle updatedNcmpServiceCmHandle) { + setAndUpdateCmHandleField( + updatedNcmpServiceCmHandle.getCmHandleId(), "alternate-id", updatedNcmpServiceCmHandle.getAlternateId()); + updateDataProducerIdentifier(existingCmHandleDataNode, updatedNcmpServiceCmHandle); + if (!updatedNcmpServiceCmHandle.getPublicProperties().isEmpty()) { + updateProperties(existingCmHandleDataNode, PUBLIC_PROPERTY, + updatedNcmpServiceCmHandle.getPublicProperties()); + } + if (!updatedNcmpServiceCmHandle.getDmiProperties().isEmpty()) { + updateProperties(existingCmHandleDataNode, DMI_PROPERTY, updatedNcmpServiceCmHandle.getDmiProperties()); + } + } + + private void updateDataProducerIdentifier(final DataNode cmHandleDataNode, + final NcmpServiceCmHandle ncmpServiceCmHandle) { + final String newDataProducerIdentifier = ncmpServiceCmHandle.getDataProducerIdentifier(); + if (StringUtils.hasText(newDataProducerIdentifier)) { + final YangModelCmHandle yangModelCmHandle = + YangDataConverter.convertCmHandleToYangModel(cmHandleDataNode); + final String existingDataProducerIdentifier = yangModelCmHandle.getDataProducerIdentifier(); + if (StringUtils.hasText(existingDataProducerIdentifier)) { + if (!existingDataProducerIdentifier.equals(newDataProducerIdentifier)) { + log.warn("Unable to update dataProducerIdentifier for cmHandle {}. " + + "Value for dataProducerIdentifier has been set previously.", + ncmpServiceCmHandle.getCmHandleId()); + } else { + log.debug("dataProducerIdentifier for cmHandle {} is already set to {}.", + ncmpServiceCmHandle.getCmHandleId(), newDataProducerIdentifier); + } + } else { + setAndUpdateCmHandleField( + yangModelCmHandle.getId(), "data-producer-identifier", newDataProducerIdentifier); + } + } + } + + private void updateProperties(final DataNode existingCmHandleDataNode, final PropertyType propertyType, + final Map updatedProperties) { + final Collection replacementPropertyDataNodes = + getReplacementDataNodes(existingCmHandleDataNode, propertyType, updatedProperties); + replacementPropertyDataNodes.addAll( + getUnchangedPropertyDataNodes(existingCmHandleDataNode, propertyType, updatedProperties)); + if (replacementPropertyDataNodes.isEmpty()) { + removeAllProperties(existingCmHandleDataNode, propertyType); + } else { + inventoryPersistence.replaceListContent(existingCmHandleDataNode.getXpath(), replacementPropertyDataNodes); + } + } + + private void removeAllProperties(final DataNode existingCmHandleDataNode, final PropertyType propertyType) { + existingCmHandleDataNode.getChildDataNodes().forEach(dataNode -> { + final Matcher matcher = propertyType.propertyXpathPattern.matcher(dataNode.getXpath()); + if (matcher.find()) { + log.info("Deleting dataNode with xpath : [{}]", dataNode.getXpath()); + inventoryPersistence.deleteDataNode(dataNode.getXpath()); + } + }); + } + + private Collection getUnchangedPropertyDataNodes(final DataNode existingCmHandleDataNode, + final PropertyType propertyType, + final Map updatedProperties) { + final Collection unchangedPropertyDataNodes = new HashSet<>(); + for (final DataNode existingPropertyDataNode : existingCmHandleDataNode.getChildDataNodes()) { + final Matcher matcher = propertyType.propertyXpathPattern.matcher(existingPropertyDataNode.getXpath()); + if (matcher.find()) { + final String keyName = matcher.group(2); + if (!updatedProperties.containsKey(keyName)) { + unchangedPropertyDataNodes.add(existingPropertyDataNode); + } + } + } + return unchangedPropertyDataNodes; + } + + private Collection getReplacementDataNodes(final DataNode existingCmHandleDataNode, + final PropertyType propertyType, + final Map updatedProperties) { + final Collection replacementPropertyDataNodes = new HashSet<>(); + updatedProperties.forEach((updatedAttributeKey, updatedAttributeValue) -> { + final String propertyXpath = getAttributeXpath(existingCmHandleDataNode, propertyType, updatedAttributeKey); + if (updatedAttributeValue != null) { + log.info("Creating a new DataNode with xpath {} , key : {} and value : {}", propertyXpath, + updatedAttributeKey, updatedAttributeValue); + replacementPropertyDataNodes.add( + buildDataNode(propertyXpath, updatedAttributeKey, updatedAttributeValue)); + } + }); + return replacementPropertyDataNodes; + } + + private String getAttributeXpath(final DataNode cmHandle, final PropertyType propertyType, + final String attributeKey) { + return cmHandle.getXpath() + "/" + propertyType.xpathPrefix + String.format("[@name='%s']", attributeKey); + } + + private DataNode buildDataNode(final String xpath, final String attributeKey, final String attributeValue) { + final Map updatedLeaves = new LinkedHashMap<>(1); + updatedLeaves.put("name", attributeKey); + updatedLeaves.put("value", attributeValue); + log.debug("Building a new node with xpath {} with leaves (name : {} , value : {})", xpath, attributeKey, + attributeValue); + return new DataNodeBuilder().withXpath(xpath).withLeaves(ImmutableMap.copyOf(updatedLeaves)).build(); + } + + private void setAndUpdateCmHandleField(final String cmHandleIdToUpdate, final String fieldName, + final String newFieldValue) { + final Map> dmiRegistryData = new HashMap<>(1); + final Map cmHandleData = new HashMap<>(2); + cmHandleData.put("id", cmHandleIdToUpdate); + cmHandleData.put(fieldName, newFieldValue); + dmiRegistryData.put("cm-handles", cmHandleData); + cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT, + jsonObjectMapper.asJsonString(dmiRegistryData), OffsetDateTime.now(), ContentType.JSON); + log.debug("Updating {} for cmHandle {} with value : {})", fieldName, cmHandleIdToUpdate, newFieldValue); + } + + enum PropertyType { + DMI_PROPERTY("additional-properties"), PUBLIC_PROPERTY("public-properties"); + + private static final String LIST_INDEX_PATTERN = "\\[@(\\w+)[^\\/]'([^']+)']"; + + final String xpathPrefix; + final Pattern propertyXpathPattern; + + PropertyType(final String xpathPrefix) { + this.xpathPrefix = xpathPrefix; + this.propertyXpathPattern = Pattern.compile(xpathPrefix + LIST_INDEX_PATTERN); + } + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpCachedResourceRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpCachedResourceRequestHandler.java index eb43718f02..da230cf732 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpCachedResourceRequestHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpCachedResourceRequestHandler.java @@ -22,7 +22,7 @@ package org.onap.cps.ncmp.api.impl; import java.util.Collection; import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; +import org.onap.cps.api.CpsDataService; import org.onap.cps.ncmp.api.NetworkCmProxyQueryService; import org.onap.cps.ncmp.api.models.CmResourceAddress; import org.onap.cps.spi.FetchDescendantsOption; @@ -34,7 +34,7 @@ import reactor.core.publisher.Mono; @RequiredArgsConstructor public class NcmpCachedResourceRequestHandler extends NcmpDatastoreRequestHandler { - private final NetworkCmProxyDataService networkCmProxyDataService; + private final CpsDataService cpsDataService; private final NetworkCmProxyQueryService networkCmProxyQueryService; /** @@ -61,8 +61,12 @@ public class NcmpCachedResourceRequestHandler extends NcmpDatastoreRequestHandle final boolean includeDescendants, final String authorization) { final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendants); - return Mono.fromSupplier( - () -> networkCmProxyDataService.getResourceDataForCmHandle(cmResourceAddress, fetchDescendantsOption)); + + final DataNode dataNode = cpsDataService.getDataNodes(cmResourceAddress.datastoreName(), + cmResourceAddress.cmHandleId(), + cmResourceAddress.resourceIdentifier(), + fetchDescendantsOption).iterator().next(); + return Mono.justOrEmpty(dataNode); } private static FetchDescendantsOption getFetchDescendantsOption(final boolean includeDescendants) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpDatastoreRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpDatastoreRequestHandler.java index dbd2bb4938..302ba449c7 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpDatastoreRequestHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpDatastoreRequestHandler.java @@ -45,46 +45,43 @@ public abstract class NcmpDatastoreRequestHandler { * Executes synchronous/asynchronous get request for given cm handle. * * @param cmResourceAddress the name of the datastore, cm handle and resource identifier - * @param optionsParamInQuery the options param in query - * @param topicParamInQuery the topic param in query + * @param options options to pass through to dmi client + * @param topic topic (optional) for asynchronous responses * @param includeDescendants whether include descendants * @param authorization contents of Authorization header, or null if not present * @return the result object, depends on use op topic. With topic a map object with request id is returned * otherwise the result of the request. */ public Object executeRequest(final CmResourceAddress cmResourceAddress, - final String optionsParamInQuery, - final String topicParamInQuery, - final boolean includeDescendants, - final String authorization) { + final String options, + final String topic, + final boolean includeDescendants, + final String authorization) { - final boolean asyncResponseRequested = topicParamInQuery != null; + final boolean asyncResponseRequested = topic != null; if (asyncResponseRequested && notificationFeatureEnabled) { - return fetchResourceDataAsynchronously(cmResourceAddress, optionsParamInQuery, topicParamInQuery, - includeDescendants, authorization); + return getResourceDataAsynchronously(cmResourceAddress, options, topic, includeDescendants, authorization); } if (asyncResponseRequested) { log.warn("Asynchronous request is unavailable as notification feature is currently disabled, " + "will use synchronous operation."); } - final Mono resourceDataMono = getResourceDataForCmHandle(cmResourceAddress, optionsParamInQuery, + final Mono resourceDataMono = getResourceDataForCmHandle(cmResourceAddress, options, NO_TOPIC, NO_REQUEST_ID, includeDescendants, authorization); return resourceDataMono.block(); } - private Map fetchResourceDataAsynchronously(final CmResourceAddress cmResourceAddress, - final String optionsParamInQuery, - final String topicParamInQuery, - final boolean includeDescendants, - final String authorization) { - TopicValidator.validateTopicName(topicParamInQuery); + private Map getResourceDataAsynchronously(final CmResourceAddress cmResourceAddress, + final String options, + final String topic, + final boolean includeDescendants, + final String authorization) { + TopicValidator.validateTopicName(topic); final String requestId = UUID.randomUUID().toString(); - getResourceDataForCmHandle(cmResourceAddress, optionsParamInQuery, topicParamInQuery, requestId, - includeDescendants, authorization) - .doOnSuccess(result -> log.debug("Async operation succeeded for request id {}: {}", requestId, result)) - .doOnError(error -> - log.error("Async operation failed for request id {}: {}", requestId, error.getMessage())) + getResourceDataForCmHandle(cmResourceAddress, options, topic, requestId, includeDescendants, authorization) + .doOnSuccess(result -> + log.debug("Async operation succeeded for request id {}: {}", requestId, result)) .subscribe(); log.debug("Received Async request with id {}", requestId); return Map.of("requestId", requestId); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpPassthroughResourceRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpPassthroughResourceRequestHandler.java index 90d9a23d6d..0fd32501c3 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpPassthroughResourceRequestHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NcmpPassthroughResourceRequestHandler.java @@ -26,9 +26,9 @@ import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ; import java.util.Map; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; import org.onap.cps.ncmp.api.impl.exception.InvalidDatastoreException; import org.onap.cps.ncmp.api.impl.operations.DatastoreType; +import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; import org.onap.cps.ncmp.api.impl.operations.OperationType; import org.onap.cps.ncmp.api.models.CmResourceAddress; import org.onap.cps.ncmp.api.models.DataOperationRequest; @@ -42,60 +42,60 @@ import reactor.core.publisher.Mono; @RequiredArgsConstructor public class NcmpPassthroughResourceRequestHandler extends NcmpDatastoreRequestHandler { - private final NetworkCmProxyDataService networkCmProxyDataService; + private final DmiDataOperations dmiDataOperations; + private static final int MAXIMUM_CM_HANDLES_PER_OPERATION = 200; private static final String PAYLOAD_TOO_LARGE_TEMPLATE = "Operation '%s' affects too many (%d) cm handles"; /** * Executes asynchronous request for group of cm handles to resource data. * - * @param topicParamInQuery the topic param in query - * @param dataOperationRequest data operation request details for resource data - * @param authorization contents of Authorization header, or null if not present + * @param topic the topic param in query + * @param dataOperationRequest data operation request details for resource data + * @param authorization contents of Authorization header, or null if not present * @return a map with one entry of request Id for success or status and error when async feature is disabled */ - public Map executeRequest(final String topicParamInQuery, - final DataOperationRequest dataOperationRequest, - final String authorization) { - validateDataOperationRequest(topicParamInQuery, dataOperationRequest); + public Map executeAsynchronousRequest(final String topic, + final DataOperationRequest dataOperationRequest, + final String authorization) { + validateDataOperationRequest(topic, dataOperationRequest); if (!notificationFeatureEnabled) { return Map.of("status", "Asynchronous request is unavailable as notification feature is currently disabled."); } final String requestId = UUID.randomUUID().toString(); - networkCmProxyDataService.executeDataOperationForCmHandles(topicParamInQuery, dataOperationRequest, requestId, - authorization); + dmiDataOperations.requestResourceDataFromDmi(topic, dataOperationRequest, requestId, authorization); return Map.of("requestId", requestId); - } @Override protected Mono getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, - final String optionsParamInQuery, - final String topicParamInQuery, + final String options, + final String topic, final String requestId, final boolean includeDescendants, final String authorization) { - return networkCmProxyDataService.getResourceDataForCmHandle(cmResourceAddress, optionsParamInQuery, - topicParamInQuery, requestId, authorization); + + return dmiDataOperations.getResourceDataFromDmi(cmResourceAddress, options, topic, requestId, authorization) + .flatMap(responseEntity -> Mono.justOrEmpty(responseEntity.getBody())); } private void validateDataOperationRequest(final String topicParamInQuery, final DataOperationRequest dataOperationRequest) { TopicValidator.validateTopicName(topicParamInQuery); - dataOperationRequest.getDataOperationDefinitions().forEach(dataOperationDetail -> { - if (OperationType.fromOperationName(dataOperationDetail.getOperation()) != READ) { + dataOperationRequest.getDataOperationDefinitions().forEach(dataOperationDefinition -> { + if (OperationType.fromOperationName(dataOperationDefinition.getOperation()) != READ) { throw new OperationNotSupportedException( - dataOperationDetail.getOperation() + " operation not yet supported"); + dataOperationDefinition.getOperation() + " operation not yet supported"); } - if (DatastoreType.fromDatastoreName(dataOperationDetail.getDatastore()) == OPERATIONAL) { - throw new InvalidDatastoreException(dataOperationDetail.getDatastore() + if (DatastoreType.fromDatastoreName(dataOperationDefinition.getDatastore()) == OPERATIONAL) { + throw new InvalidDatastoreException(dataOperationDefinition.getDatastore() + " datastore is not supported"); } - if (dataOperationDetail.getCmHandleIds().size() > MAXIMUM_CM_HANDLES_PER_OPERATION) { + if (dataOperationDefinition.getCmHandleIds().size() > MAXIMUM_CM_HANDLES_PER_OPERATION) { final String errorMessage = String.format(PAYLOAD_TOO_LARGE_TEMPLATE, - dataOperationDetail.getOperationId(), - dataOperationDetail.getCmHandleIds().size()); + dataOperationDefinition.getOperationId(), + dataOperationDefinition.getCmHandleIds().size()); throw new PayloadTooLargeException(errorMessage); } }); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java deleted file mode 100644 index 8890d14ae1..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl; - -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; -import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_MODULES; -import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_PROPERTIES; -import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_CPS_PATH; -import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_TRUST_LEVEL; -import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCpsPathConditionProperties; -import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateModuleNameConditionProperties; -import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle; -import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY; -import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.cpspath.parser.PathParsingException; -import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType; -import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -import org.onap.cps.spi.exceptions.DataValidationException; -import org.onap.cps.spi.model.ConditionProperties; -import org.onap.cps.spi.model.DataNode; -import org.springframework.stereotype.Service; - -@Service -@Slf4j -@RequiredArgsConstructor -public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmHandleQueryService { - - private static final Collection NO_QUERY_TO_EXECUTE = null; - private final CmHandleQueries cmHandleQueries; - private final InventoryPersistence inventoryPersistence; - - @Override - public Collection queryCmHandleIds( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - return executeQueries(cmHandleQueryServiceParameters, - this::executeCpsPathQuery, - this::queryCmHandlesByPublicProperties, - this::executeModuleNameQuery, - this::queryCmHandlesByTrustLevel); - } - - @Override - public Collection queryCmHandleIdsForInventory( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - return executeQueries(cmHandleQueryServiceParameters, - this::queryCmHandlesByPublicProperties, - this::queryCmHandlesByPrivateProperties, - this::queryCmHandlesByDmiPlugin); - } - - @Override - public Collection queryCmHandles( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - - if (cmHandleQueryServiceParameters.getCmHandleQueryParameters().isEmpty()) { - return getAllCmHandles(); - } - - final Collection cmHandleIds = queryCmHandleIds(cmHandleQueryServiceParameters); - - return getNcmpServiceCmHandles(cmHandleIds); - } - - @Override - public Collection getAllCmHandles() { - final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT).iterator().next(); - return dataNode.getChildDataNodes().stream().map(this::createNcmpServiceCmHandle).collect(Collectors.toSet()); - } - - private Collection queryCmHandlesByDmiPlugin( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - final Map dmiPropertyQueryPairs = - getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), - InventoryQueryConditions.CM_HANDLE_WITH_DMI_PLUGIN.getName()); - if (dmiPropertyQueryPairs.isEmpty()) { - return NO_QUERY_TO_EXECUTE; - } - - final String dmiPluginIdentifierValue = dmiPropertyQueryPairs - .get(PropertyType.DMI_PLUGIN.getYangContainerName()); - - return cmHandleQueries.getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifierValue); - } - - private Collection queryCmHandlesByPrivateProperties( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - - final Map privatePropertyQueryPairs = - getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), - InventoryQueryConditions.HAS_ALL_ADDITIONAL_PROPERTIES.getName()); - - if (privatePropertyQueryPairs.isEmpty()) { - return NO_QUERY_TO_EXECUTE; - } - return cmHandleQueries.queryCmHandleAdditionalProperties(privatePropertyQueryPairs); - } - - private Collection queryCmHandlesByPublicProperties( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - - final Map publicPropertyQueryPairs = - getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), - HAS_ALL_PROPERTIES.getConditionName()); - - if (publicPropertyQueryPairs.isEmpty()) { - return NO_QUERY_TO_EXECUTE; - } - return cmHandleQueries.queryCmHandlePublicProperties(publicPropertyQueryPairs); - } - - private Collection queryCmHandlesByTrustLevel(final CmHandleQueryServiceParameters - cmHandleQueryServiceParameters) { - - final Map trustLevelPropertyQueryPairs = - getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), - WITH_TRUST_LEVEL.getConditionName()); - - if (trustLevelPropertyQueryPairs.isEmpty()) { - return NO_QUERY_TO_EXECUTE; - } - return cmHandleQueries.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs); - } - - private Collection executeModuleNameQuery( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - final Collection moduleNamesForQuery = - getModuleNamesForQuery(cmHandleQueryServiceParameters.getCmHandleQueryParameters()); - if (moduleNamesForQuery.isEmpty()) { - return NO_QUERY_TO_EXECUTE; - } - return inventoryPersistence.getCmHandleIdsWithGivenModules(moduleNamesForQuery); - } - - private Collection executeCpsPathQuery( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - final Map cpsPathCondition - = getCpsPathCondition(cmHandleQueryServiceParameters.getCmHandleQueryParameters()); - if (!validateCpsPathConditionProperties(cpsPathCondition)) { - return Collections.emptySet(); - } - final Collection cpsPathQueryResult; - if (cpsPathCondition.isEmpty()) { - return NO_QUERY_TO_EXECUTE; - } - try { - cpsPathQueryResult = collectCmHandleIdsFromDataNodes( - cmHandleQueries.queryCmHandleAncestorsByCpsPath( - cpsPathCondition.get("cpsPath"), OMIT_DESCENDANTS)); - } catch (final PathParsingException pathParsingException) { - throw new DataValidationException(pathParsingException.getMessage(), pathParsingException.getDetails(), - pathParsingException); - } - return cpsPathQueryResult; - } - - private Collection getModuleNamesForQuery(final List conditionProperties) { - final List result = new ArrayList<>(); - getConditions(conditionProperties, HAS_ALL_MODULES.getConditionName()).forEach( - conditionProperty -> { - validateModuleNameConditionProperties(conditionProperty); - result.add(conditionProperty.get("moduleName")); - }); - return result; - } - - private Map getCpsPathCondition(final List conditionProperties) { - final Map result = new HashMap<>(); - getConditions(conditionProperties, WITH_CPS_PATH.getConditionName()).forEach(result::putAll); - return result; - } - - private Map getPropertyPairs(final List conditionProperties, - final String queryProperty) { - final Map result = new HashMap<>(); - getConditions(conditionProperties, queryProperty).forEach(result::putAll); - return result; - } - - private List> getConditions(final List conditionProperties, - final String name) { - for (final ConditionProperties conditionProperty : conditionProperties) { - if (conditionProperty.getConditionName().equals(name)) { - return conditionProperty.getConditionParameters(); - } - } - return Collections.emptyList(); - } - - private Collection getAllCmHandleIds() { - final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT, DIRECT_CHILDREN_ONLY) - .iterator().next(); - return collectCmHandleIdsFromDataNodes(dataNode.getChildDataNodes()); - } - - private Collection getNcmpServiceCmHandles(final Collection cmHandleIds) { - final Collection yangModelcmHandles - = inventoryPersistence.getYangModelCmHandles(cmHandleIds); - - final Collection ncmpServiceCmHandles = new ArrayList<>(yangModelcmHandles.size()); - - yangModelcmHandles.forEach(yangModelcmHandle -> - ncmpServiceCmHandles.add(YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle(yangModelcmHandle)) - ); - return ncmpServiceCmHandles; - } - - private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) { - return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter.convertCmHandleToYangModel(dataNode)); - } - - private Collection executeQueries(final CmHandleQueryServiceParameters cmHandleQueryServiceParameters, - final Function>... - queryFunctions) { - if (cmHandleQueryServiceParameters.getCmHandleQueryParameters().isEmpty()) { - return getAllCmHandleIds(); - } - Collection combinedQueryResult = NO_QUERY_TO_EXECUTE; - for (final Function> queryFunction : queryFunctions) { - final Collection queryResult = queryFunction.apply(cmHandleQueryServiceParameters); - if (noEntriesFoundCanStopQuerying(queryResult)) { - return Collections.emptySet(); - } - combinedQueryResult = combineCmHandleQueryResults(combinedQueryResult, queryResult); - } - return combinedQueryResult; - } - - private boolean noEntriesFoundCanStopQuerying(final Collection queryResult) { - return queryResult != NO_QUERY_TO_EXECUTE && queryResult.isEmpty(); - } - - private Collection combineCmHandleQueryResults(final Collection firstQuery, - final Collection secondQuery) { - if (firstQuery == NO_QUERY_TO_EXECUTE && secondQuery == NO_QUERY_TO_EXECUTE) { - return NO_QUERY_TO_EXECUTE; - } else if (firstQuery == NO_QUERY_TO_EXECUTE) { - return secondQuery; - } else if (secondQuery == NO_QUERY_TO_EXECUTE) { - return firstQuery; - } else { - firstQuery.retainAll(secondQuery); - return firstQuery; - } - } - - private Collection collectCmHandleIdsFromDataNodes(final Collection dataNodes) { - return dataNodes.stream().map(dataNode -> (String) dataNode.getLeaves().get("id")).collect(Collectors.toSet()); - } - -} 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 deleted file mode 100755 index 754050947a..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ /dev/null @@ -1,568 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 highstreet technologies GmbH - * Modifications Copyright (C) 2021-2024 Nordix Foundation - * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2021-2022 Bell Canada - * Modifications Copyright (C) 2023 TechMahindra Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl; - -import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED; -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY; -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST; -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; -import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCmHandleQueryParameters; - -import com.google.common.collect.Lists; -import com.hazelcast.map.IMap; -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.onap.cps.api.CpsDataService; -import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; -import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; -import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder; -import org.onap.cps.ncmp.api.impl.inventory.CompositeStateUtils; -import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleOperationsUtils; -import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; -import org.onap.cps.ncmp.api.impl.operations.OperationType; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager; -import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker; -import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions; -import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; -import org.onap.cps.ncmp.api.models.CmResourceAddress; -import org.onap.cps.ncmp.api.models.DataOperationRequest; -import org.onap.cps.ncmp.api.models.DmiPluginRegistration; -import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -import org.onap.cps.spi.FetchDescendantsOption; -import org.onap.cps.spi.exceptions.AlreadyDefinedException; -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.ModuleDefinition; -import org.onap.cps.spi.model.ModuleReference; -import org.onap.cps.utils.JsonObjectMapper; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; -import reactor.core.publisher.Mono; - -@Slf4j -@Service -@RequiredArgsConstructor -public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService { - - private static final int DELETE_BATCH_SIZE = 100; - private final JsonObjectMapper jsonObjectMapper; - private final DmiDataOperations dmiDataOperations; - private final NetworkCmProxyDataServicePropertyHandler networkCmProxyDataServicePropertyHandler; - private final InventoryPersistence inventoryPersistence; - private final CmHandleQueries cmHandleQueries; - private final NetworkCmProxyCmHandleQueryService networkCmProxyCmHandleQueryService; - private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler; - private final CpsDataService cpsDataService; - private final IMap moduleSyncStartedOnCmHandles; - - @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN) - private final Map trustLevelPerDmiPlugin; - - private final TrustLevelManager trustLevelManager; - private final AlternateIdChecker alternateIdChecker; - - @Override - public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( - final DmiPluginRegistration dmiPluginRegistration) { - - dmiPluginRegistration.validateDmiPluginRegistration(); - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse(); - - setTrustLevelPerDmiPlugin(dmiPluginRegistration); - - processRemovedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); - - processCreatedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); - - processUpdatedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); - - processUpgradedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); - - return dmiPluginRegistrationResponse; - } - - @Override - public Mono getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final String authorization) { - return dmiDataOperations.getResourceDataFromDmi(cmResourceAddress, optionsParamInQuery, topicParamInQuery, - requestId, authorization) - .flatMap(responseEntity -> Mono.justOrEmpty(responseEntity.getBody())); - } - - @Override - public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, - final FetchDescendantsOption fetchDescendantsOption) { - return cpsDataService.getDataNodes(cmResourceAddress.datastoreName(), - cmResourceAddress.cmHandleId(), - cmResourceAddress.resourceIdentifier(), - fetchDescendantsOption).iterator().next(); - } - - @Override - public void executeDataOperationForCmHandles(final String topicParamInQuery, - final DataOperationRequest dataOperationRequest, - final String requestId, - final String authorization) { - dmiDataOperations.requestResourceDataFromDmi(topicParamInQuery, dataOperationRequest, requestId, authorization); - } - - @Override - public Object writeResourceDataPassThroughRunningForCmHandle(final String cmHandleId, - final String resourceIdentifier, - final OperationType operationType, - final String requestData, - final String dataType, - final String authorization) { - return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, - operationType, requestData, dataType, authorization); - } - - @Override - public Collection getYangResourcesModuleReferences(final String cmHandleId) { - return inventoryPersistence.getYangResourcesModuleReferences(cmHandleId); - } - - @Override - public Collection getModuleDefinitionsByCmHandleId(final String cmHandleId) { - return inventoryPersistence.getModuleDefinitionsByCmHandleId(cmHandleId); - } - - @Override - public Collection getModuleDefinitionsByCmHandleAndModule(final String cmHandleId, - final String moduleName, - final String moduleRevision) { - return inventoryPersistence.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, moduleRevision); - } - - /** - * Retrieve cm handles with details for the given query parameters. - * - * @param cmHandleQueryApiParameters cm handle query parameters - * @return cm handles with details - */ - @Override - public Collection executeCmHandleSearch( - final CmHandleQueryApiParameters cmHandleQueryApiParameters) { - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( - cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); - validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); - return networkCmProxyCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters); - } - - /** - * Retrieve cm handle ids for the given query parameters. - * - * @param cmHandleQueryApiParameters cm handle query parameters - * @return cm handle ids - */ - @Override - public Collection executeCmHandleIdSearch(final CmHandleQueryApiParameters cmHandleQueryApiParameters) { - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( - cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); - validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); - return networkCmProxyCmHandleQueryService.queryCmHandleIds(cmHandleQueryServiceParameters); - } - - /** - * Set the data sync enabled flag, along with the data sync state - * based on the data sync enabled boolean for the cm handle id provided. - * - * @param cmHandleId cm handle id - * @param dataSyncEnabledTargetValue data sync enabled flag - */ - @Override - public void setDataSyncEnabled(final String cmHandleId, final Boolean dataSyncEnabledTargetValue) { - final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); - if (dataSyncEnabledTargetValue.equals(compositeState.getDataSyncEnabled())) { - log.info("Data-Sync Enabled flag is already: {} ", dataSyncEnabledTargetValue); - return; - } - if (CmHandleState.READY.equals(compositeState.getCmHandleState())) { - final DataStoreSyncState dataStoreSyncState = compositeState.getDataStores() - .getOperationalDataStore().getDataStoreSyncState(); - if (Boolean.FALSE.equals(dataSyncEnabledTargetValue) - && DataStoreSyncState.SYNCHRONIZED.equals(dataStoreSyncState)) { - // TODO : This is hard-coded for onap dmi that need to be addressed - cpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, - "/netconf-state", OffsetDateTime.now()); - } - CompositeStateUtils.setDataSyncEnabledFlagWithDataSyncState(dataSyncEnabledTargetValue, compositeState); - inventoryPersistence.saveCmHandleState(cmHandleId, compositeState); - } else { - throw new CpsException("State mismatch exception.", "Cm-Handle not in READY state. Cm handle state is: " - + compositeState.getCmHandleState()); - } - } - - /** - * Get all cm handle IDs by DMI plugin identifier. - * - * @param dmiPluginIdentifier DMI plugin identifier - * @return set of cm handle IDs - */ - @Override - public Collection getAllCmHandleIdsByDmiPluginIdentifier(final String dmiPluginIdentifier) { - return cmHandleQueries.getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifier); - } - - /** - * Get all cm handle IDs by various properties. - * - * @param cmHandleQueryServiceParameters cm handle query parameters - * @return set of cm handle IDs - */ - @Override - public Collection executeCmHandleIdSearchForInventory( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - validateCmHandleQueryParameters(cmHandleQueryServiceParameters, InventoryQueryConditions.ALL_CONDITION_NAMES); - return networkCmProxyCmHandleQueryService.queryCmHandleIdsForInventory(cmHandleQueryServiceParameters); - } - - /** - * Retrieve cm handle details for a given cm handle. - * - * @param cmHandleId cm handle identifier - * @return cm handle details - */ - @Override - public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleId) { - return YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle( - inventoryPersistence.getYangModelCmHandle(cmHandleId)); - } - - /** - * Get cm handle public properties for a given cm handle id. - * - * @param cmHandleId cm handle identifier - * @return cm handle public properties - */ - @Override - public Map getCmHandlePublicProperties(final String cmHandleId) { - final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); - final List yangModelPublicProperties = yangModelCmHandle.getPublicProperties(); - final Map cmHandlePublicProperties = new HashMap<>(); - YangDataConverter.asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties); - return cmHandlePublicProperties; - } - - /** - * Get cm handle composite state for a given cm handle id. - * - * @param cmHandleId cm handle identifier - * @return cm handle state - */ - @Override - public CompositeState getCmHandleCompositeState(final String cmHandleId) { - return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState(); - } - - protected void processRemovedCmHandles(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { - final List tobeRemovedCmHandleIds = dmiPluginRegistration.getRemovedCmHandles(); - final List cmHandleRegistrationResponses = - new ArrayList<>(tobeRemovedCmHandleIds.size()); - final Collection yangModelCmHandles = - inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandleIds); - updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING); - - final Set notDeletedCmHandles = new HashSet<>(); - for (final List tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandleIds, DELETE_BATCH_SIZE)) { - try { - batchDeleteCmHandlesFromDbAndModuleSyncMap(tobeRemovedCmHandleBatch); - tobeRemovedCmHandleBatch.forEach(cmHandleId -> - cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId))); - - } catch (final RuntimeException batchException) { - log.error("Unable to de-register cm-handle batch, retrying on each cm handle"); - for (final String cmHandleId : tobeRemovedCmHandleBatch) { - final CmHandleRegistrationResponse cmHandleRegistrationResponse = - deleteCmHandleAndGetCmHandleRegistrationResponse(cmHandleId); - cmHandleRegistrationResponses.add(cmHandleRegistrationResponse); - if (cmHandleRegistrationResponse.getStatus() != CmHandleRegistrationResponse.Status.SUCCESS) { - notDeletedCmHandles.add(cmHandleId); - } - } - } - } - yangModelCmHandles.removeIf(yangModelCmHandle -> notDeletedCmHandles.contains(yangModelCmHandle.getId())); - updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETED); - dmiPluginRegistrationResponse.setRemovedCmHandles(cmHandleRegistrationResponses); - } - - protected void processCreatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { - final List ncmpServiceCmHandles = dmiPluginRegistration.getCreatedCmHandles(); - final List failedCmHandleRegistrationResponses = new ArrayList<>(); - - try { - final Collection rejectedCmHandleIds - = checkAlternateIds(ncmpServiceCmHandles, failedCmHandleRegistrationResponses); - - final Collection succeededCmHandleIds = persistCmHandlesWithState(dmiPluginRegistration, - dmiPluginRegistrationResponse, ncmpServiceCmHandles, rejectedCmHandleIds); - - processTrustLevels(ncmpServiceCmHandles, succeededCmHandleIds); - - } catch (final AlreadyDefinedException alreadyDefinedException) { - failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponsesFromXpaths( - alreadyDefinedException.getAlreadyDefinedObjectNames(), CM_HANDLE_ALREADY_EXIST)); - } catch (final Exception exception) { - final Collection cmHandleIds = - ncmpServiceCmHandles.stream().map(NcmpServiceCmHandle::getCmHandleId).collect(Collectors.toList()); - failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse - .createFailureResponses(cmHandleIds, exception)); - } - final List mergedCmHandleRegistrationResponses - = new ArrayList<>(failedCmHandleRegistrationResponses); - mergedCmHandleRegistrationResponses.addAll(dmiPluginRegistrationResponse.getCreatedCmHandles()); - - dmiPluginRegistrationResponse.setCreatedCmHandles(mergedCmHandleRegistrationResponses); - } - - protected void processUpdatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { - dmiPluginRegistrationResponse.setUpdatedCmHandles(networkCmProxyDataServicePropertyHandler - .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles())); - } - - protected void processUpgradedCmHandles( - final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { - - final List cmHandleIds = dmiPluginRegistration.getUpgradedCmHandles().getCmHandles(); - final String upgradedModuleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag(); - final Map acceptedCmHandleStatePerCmHandle - = new HashMap<>(cmHandleIds.size()); - final List cmHandleUpgradeResponses = new ArrayList<>(cmHandleIds.size()); - - for (final String cmHandleId : cmHandleIds) { - try { - final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); - if (yangModelCmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY) { - if (moduleUpgradeCanBeSkipped(yangModelCmHandle, upgradedModuleSetTag)) { - cmHandleUpgradeResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)); - } else { - updateYangModelCmHandleForUpgrade(yangModelCmHandle, upgradedModuleSetTag); - acceptedCmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED); - } - } else { - cmHandleUpgradeResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_READY)); - } - } catch (final DataNodeNotFoundException dataNodeNotFoundException) { - log.error("Unable to find data node for cm handle id : {} , caused by : {}", - cmHandleId, dataNodeNotFoundException.getMessage()); - cmHandleUpgradeResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND)); - } catch (final DataValidationException dataValidationException) { - log.error("Unable to upgrade cm handle id: {}, caused by : {}", - cmHandleId, dataValidationException.getMessage()); - cmHandleUpgradeResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID)); - } - } - cmHandleUpgradeResponses.addAll(upgradeCmHandles(acceptedCmHandleStatePerCmHandle)); - dmiPluginRegistrationResponse.setUpgradedCmHandles(cmHandleUpgradeResponses); - } - - private Collection checkAlternateIds( - final List cmHandlesToBeCreated, - final List cmHandleRegistrationResponses) { - final Collection rejectedCmHandleIds = alternateIdChecker - .getIdsOfCmHandlesWithRejectedAlternateId(cmHandlesToBeCreated, AlternateIdChecker.Operation.CREATE); - cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses( - rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED)); - return rejectedCmHandleIds; - } - - private List persistCmHandlesWithState(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse, - final List cmHandlesToBeCreated, - final Collection rejectedCmHandleIds) { - final List succeededCmHandleIds = new ArrayList<>(cmHandlesToBeCreated.size()); - final List yangModelCmHandlesToRegister = new ArrayList<>(cmHandlesToBeCreated.size()); - final List cmHandleRegistrationResponses = - new ArrayList<>(cmHandlesToBeCreated.size()); - for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) { - if (!rejectedCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) { - yangModelCmHandlesToRegister.add(getYangModelCmHandle(dmiPluginRegistration, ncmpServiceCmHandle)); - cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createSuccessResponse(ncmpServiceCmHandle.getCmHandleId())); - succeededCmHandleIds.add(ncmpServiceCmHandle.getCmHandleId()); - } - } - lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandlesToRegister); - dmiPluginRegistrationResponse.setCreatedCmHandles(cmHandleRegistrationResponses); - return succeededCmHandleIds; - } - - private YangModelCmHandle getYangModelCmHandle(final DmiPluginRegistration dmiPluginRegistration, - final NcmpServiceCmHandle ncmpServiceCmHandle) { - return YangModelCmHandle.toYangModelCmHandle( - dmiPluginRegistration.getDmiPlugin(), - dmiPluginRegistration.getDmiDataPlugin(), - dmiPluginRegistration.getDmiModelPlugin(), - ncmpServiceCmHandle, - ncmpServiceCmHandle.getModuleSetTag(), - ncmpServiceCmHandle.getAlternateId(), - ncmpServiceCmHandle.getDataProducerIdentifier()); - } - - private void processTrustLevels(final Collection cmHandlesToBeCreated, - final Collection succeededCmHandleIds) { - final Map initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size()); - for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) { - if (succeededCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) { - initialTrustLevelPerCmHandleId.put(ncmpServiceCmHandle.getCmHandleId(), - ncmpServiceCmHandle.getRegistrationTrustLevel()); - } - } - trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId); - } - - private static boolean moduleUpgradeCanBeSkipped(final YangModelCmHandle yangModelCmHandle, - final String upgradedModuleSetTag) { - if (StringUtils.isBlank(upgradedModuleSetTag)) { - return false; - } - return yangModelCmHandle.getModuleSetTag().equals(upgradedModuleSetTag); - } - - private static void updateYangModelCmHandleForUpgrade(final YangModelCmHandle yangModelCmHandle, - final String upgradedModuleSetTag) { - final String lockReasonWithModuleSetTag = String.format(ModuleOperationsUtils.MODULE_SET_TAG_MESSAGE_FORMAT, - upgradedModuleSetTag); - yangModelCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.READY) - .withLockReason(MODULE_UPGRADE, lockReasonWithModuleSetTag).build()); - } - - private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) { - try { - deleteCmHandleFromDbAndModuleSyncMap(cmHandleId); - return CmHandleRegistrationResponse.createSuccessResponse(cmHandleId); - } catch (final DataNodeNotFoundException dataNodeNotFoundException) { - log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", - cmHandleId, dataNodeNotFoundException.getMessage()); - return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND); - } catch (final DataValidationException dataValidationException) { - log.error("Unable to de-register cm-handle id: {}, caused by: {}", - cmHandleId, dataValidationException.getMessage()); - return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID); - } catch (final Exception exception) { - log.error("Unable to de-register cm-handle id : {} , caused by : {}", cmHandleId, exception.getMessage()); - return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception); - } - } - - private void updateCmHandleStateBatch(final Collection yangModelCmHandles, - final CmHandleState cmHandleState) { - final Map cmHandleStatePerCmHandle = new HashMap<>(yangModelCmHandles.size()); - yangModelCmHandles.forEach(yangModelCmHandle -> cmHandleStatePerCmHandle.put(yangModelCmHandle, cmHandleState)); - lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); - } - - private void deleteCmHandleFromDbAndModuleSyncMap(final String cmHandleId) { - inventoryPersistence.deleteSchemaSetWithCascade(cmHandleId); - inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']"); - removeDeletedCmHandleFromModuleSyncMap(cmHandleId); - } - - private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection cmHandleIds) { - inventoryPersistence.deleteSchemaSetsWithCascade(cmHandleIds); - inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(cmHandleIds)); - cmHandleIds.forEach(this::removeDeletedCmHandleFromModuleSyncMap); - } - - private Collection mapCmHandleIdsToXpaths(final Collection cmHandles) { - return cmHandles.stream() - .map(cmHandleId -> NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']") - .collect(Collectors.toSet()); - } - - // CPS-1239 Robustness cleaning of in progress cache - private void removeDeletedCmHandleFromModuleSyncMap(final String cmHandleId) { - if (moduleSyncStartedOnCmHandles.remove(cmHandleId) != null) { - log.debug("{} removed from in progress map", cmHandleId); - } - } - - private List upgradeCmHandles(final Map - cmHandleStatePerCmHandle) { - final List cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle); - log.info("Moving cm handles : {} into locked (for upgrade) state.", cmHandleIds); - try { - lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); - return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds); - } catch (final Exception e) { - log.error("Unable to update cmHandleIds : {} , caused by : {}", cmHandleIds, e.getMessage()); - return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, e); - } - } - - private static List getCmHandleIds(final Map cmHandleStatePerCmHandle) { - return cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId).toList(); - } - - private void setTrustLevelPerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) { - if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) { - trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiPlugin(), TrustLevel.COMPLETE); - } else { - trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiDataPlugin(), TrustLevel.COMPLETE); - } - } - -} 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 deleted file mode 100644 index 11c58235ec..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 Nordix Foundation - * Modifications Copyright (C) 2022 Bell Canada - * Modifications Copyright (C) 2024 TechMahindra Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl; - -import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED; -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; -import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY; -import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.PUBLIC_PROPERTY; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; - -import com.google.common.collect.ImmutableMap; -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.api.CpsDataService; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -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.ContentType; -import org.onap.cps.utils.JsonObjectMapper; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -@Slf4j -@Service -@RequiredArgsConstructor -//Accepting the security hotspot as the string checked is generated from inside code and not user input. -@SuppressWarnings("squid:S5852") -public class NetworkCmProxyDataServicePropertyHandler { - - private final InventoryPersistence inventoryPersistence; - private final CpsDataService cpsDataService; - private final JsonObjectMapper jsonObjectMapper; - private final AlternateIdChecker alternateIdChecker; - - /** - * Iterates over incoming updatedNcmpServiceCmHandles and update the dataNodes based on the updated attributes. - * The attributes which are not passed will remain as is. - * - * @param updatedNcmpServiceCmHandles collection of CmHandles - */ - public List updateCmHandleProperties( - final Collection updatedNcmpServiceCmHandles) { - final Collection rejectedCmHandleIds = alternateIdChecker - .getIdsOfCmHandlesWithRejectedAlternateId(updatedNcmpServiceCmHandles, AlternateIdChecker.Operation.UPDATE); - final List failureResponses = - CmHandleRegistrationResponse.createFailureResponses(rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED); - final List cmHandleRegistrationResponses = new ArrayList<>(failureResponses); - for (final NcmpServiceCmHandle updatedNcmpServiceCmHandle : updatedNcmpServiceCmHandles) { - final String cmHandleId = updatedNcmpServiceCmHandle.getCmHandleId(); - if (!rejectedCmHandleIds.contains(cmHandleId)) { - try { - final DataNode existingCmHandleDataNode = inventoryPersistence - .getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next(); - processUpdates(existingCmHandleDataNode, updatedNcmpServiceCmHandle); - cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)); - } catch (final DataNodeNotFoundException e) { - log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", cmHandleId, - e.getMessage()); - cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND)); - } catch (final DataValidationException e) { - log.error("Unable to update cm handle : {}, caused by : {}", cmHandleId, e.getMessage()); - cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID)); - } catch (final Exception exception) { - log.error("Unable to update cmHandle : {} , caused by : {}", cmHandleId, exception.getMessage()); - cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception)); - } - } - } - return cmHandleRegistrationResponses; - } - - private void processUpdates(final DataNode existingCmHandleDataNode, - final NcmpServiceCmHandle updatedNcmpServiceCmHandle) { - setAndUpdateCmHandleField( - updatedNcmpServiceCmHandle.getCmHandleId(), "alternate-id", updatedNcmpServiceCmHandle.getAlternateId()); - updateDataProducerIdentifier(existingCmHandleDataNode, updatedNcmpServiceCmHandle); - if (!updatedNcmpServiceCmHandle.getPublicProperties().isEmpty()) { - updateProperties(existingCmHandleDataNode, PUBLIC_PROPERTY, - updatedNcmpServiceCmHandle.getPublicProperties()); - } - if (!updatedNcmpServiceCmHandle.getDmiProperties().isEmpty()) { - updateProperties(existingCmHandleDataNode, DMI_PROPERTY, updatedNcmpServiceCmHandle.getDmiProperties()); - } - } - - private void updateDataProducerIdentifier(final DataNode cmHandleDataNode, - final NcmpServiceCmHandle ncmpServiceCmHandle) { - final String newDataProducerIdentifier = ncmpServiceCmHandle.getDataProducerIdentifier(); - if (StringUtils.hasText(newDataProducerIdentifier)) { - final YangModelCmHandle yangModelCmHandle = - YangDataConverter.convertCmHandleToYangModel(cmHandleDataNode); - final String existingDataProducerIdentifier = yangModelCmHandle.getDataProducerIdentifier(); - if (StringUtils.hasText(existingDataProducerIdentifier)) { - if (!existingDataProducerIdentifier.equals(newDataProducerIdentifier)) { - log.warn("Unable to update dataProducerIdentifier for cmHandle {}. " - + "Value for dataProducerIdentifier has been set previously.", - ncmpServiceCmHandle.getCmHandleId()); - } - } else { - setAndUpdateCmHandleField( - yangModelCmHandle.getId(), "data-producer-identifier", newDataProducerIdentifier); - } - } - } - - private void updateProperties(final DataNode existingCmHandleDataNode, final PropertyType propertyType, - final Map updatedProperties) { - final Collection replacementPropertyDataNodes = - getReplacementDataNodes(existingCmHandleDataNode, propertyType, updatedProperties); - replacementPropertyDataNodes.addAll( - getUnchangedPropertyDataNodes(existingCmHandleDataNode, propertyType, updatedProperties)); - if (replacementPropertyDataNodes.isEmpty()) { - removeAllProperties(existingCmHandleDataNode, propertyType); - } else { - inventoryPersistence.replaceListContent(existingCmHandleDataNode.getXpath(), replacementPropertyDataNodes); - } - } - - private void removeAllProperties(final DataNode existingCmHandleDataNode, final PropertyType propertyType) { - existingCmHandleDataNode.getChildDataNodes().forEach(dataNode -> { - final Matcher matcher = propertyType.propertyXpathPattern.matcher(dataNode.getXpath()); - if (matcher.find()) { - log.info("Deleting dataNode with xpath : [{}]", dataNode.getXpath()); - inventoryPersistence.deleteDataNode(dataNode.getXpath()); - } - }); - } - - private Collection getUnchangedPropertyDataNodes(final DataNode existingCmHandleDataNode, - final PropertyType propertyType, - final Map updatedProperties) { - final Collection unchangedPropertyDataNodes = new HashSet<>(); - for (final DataNode existingPropertyDataNode : existingCmHandleDataNode.getChildDataNodes()) { - final Matcher matcher = propertyType.propertyXpathPattern.matcher(existingPropertyDataNode.getXpath()); - if (matcher.find()) { - final String keyName = matcher.group(2); - if (!updatedProperties.containsKey(keyName)) { - unchangedPropertyDataNodes.add(existingPropertyDataNode); - } - } - } - return unchangedPropertyDataNodes; - } - - private Collection getReplacementDataNodes(final DataNode existingCmHandleDataNode, - final PropertyType propertyType, - final Map updatedProperties) { - final Collection replacementPropertyDataNodes = new HashSet<>(); - updatedProperties.forEach((updatedAttributeKey, updatedAttributeValue) -> { - final String propertyXpath = getAttributeXpath(existingCmHandleDataNode, propertyType, updatedAttributeKey); - if (updatedAttributeValue != null) { - log.info("Creating a new DataNode with xpath {} , key : {} and value : {}", propertyXpath, - updatedAttributeKey, updatedAttributeValue); - replacementPropertyDataNodes.add( - buildDataNode(propertyXpath, updatedAttributeKey, updatedAttributeValue)); - } - }); - return replacementPropertyDataNodes; - } - - private String getAttributeXpath(final DataNode cmHandle, final PropertyType propertyType, - final String attributeKey) { - return cmHandle.getXpath() + "/" + propertyType.xpathPrefix + String.format("[@name='%s']", attributeKey); - } - - private DataNode buildDataNode(final String xpath, final String attributeKey, final String attributeValue) { - final Map updatedLeaves = new LinkedHashMap<>(1); - updatedLeaves.put("name", attributeKey); - updatedLeaves.put("value", attributeValue); - log.debug("Building a new node with xpath {} with leaves (name : {} , value : {})", xpath, attributeKey, - attributeValue); - return new DataNodeBuilder().withXpath(xpath).withLeaves(ImmutableMap.copyOf(updatedLeaves)).build(); - } - - private void setAndUpdateCmHandleField(final String cmHandleIdToUpdate, final String fieldName, - final String newFieldValue) { - final Map> dmiRegistryData = new HashMap<>(1); - final Map cmHandleData = new HashMap<>(2); - cmHandleData.put("id", cmHandleIdToUpdate); - cmHandleData.put(fieldName, newFieldValue); - dmiRegistryData.put("cm-handles", cmHandleData); - cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT, - jsonObjectMapper.asJsonString(dmiRegistryData), OffsetDateTime.now(), ContentType.JSON); - log.debug("Updating {} for cmHandle {} with value : {})", fieldName, cmHandleIdToUpdate, newFieldValue); - } - - enum PropertyType { - DMI_PROPERTY("additional-properties"), PUBLIC_PROPERTY("public-properties"); - - private static final String LIST_INDEX_PATTERN = "\\[@(\\w+)[^\\/]'([^']+)']"; - - final String xpathPrefix; - final Pattern propertyXpathPattern; - - PropertyType(final String xpathPrefix) { - this.xpathPrefix = xpathPrefix; - this.propertyXpathPattern = Pattern.compile(xpathPrefix + LIST_INDEX_PATTERN); - } - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyFacade.java new file mode 100644 index 0000000000..a24b18446e --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyFacade.java @@ -0,0 +1,129 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 highstreet technologies GmbH + * Modifications Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2021-2022 Bell Canada + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl; + +import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL; + +import java.util.Collection; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.impl.operations.DatastoreType; +import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; +import org.onap.cps.ncmp.api.impl.operations.OperationType; +import org.onap.cps.ncmp.api.models.CmResourceAddress; +import org.onap.cps.ncmp.api.models.DataOperationRequest; +import org.onap.cps.spi.model.DataNode; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class NetworkCmProxyFacade { + + private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler; + private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler; + private final DmiDataOperations dmiDataOperations; + + /** + * Fetches resource data for a given data store using DMI (Data Management Interface). + * This method retrieves data based on the provided CmResourceAddress and additional query parameters. + * It supports asynchronous processing and handles authorization if required. + * + * @param cmResourceAddress The target data store, including the CM handle and resource identifier. + * This parameter must not be null. + * @param options Additional query parameters that may influence the data retrieval process, + * such as filters or limits. This parameter can be null. + * @param topic The topic name for triggering asynchronous responses. If specified, + * the response will be sent to this topic. This parameter can be null. + * @param includeDescendants include (all) descendants or not + * @param authorization The contents of the Authorization header. This parameter can be null + * if authorization is not required. + * @return the result object, depends on use op topic. With topic a map object with request id is returned + * otherwise the result of the request. + */ + public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, + final String options, + final String topic, + final Boolean includeDescendants, + final String authorization) { + final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler + = getNcmpDatastoreRequestHandler(cmResourceAddress.datastoreName()); + + return ncmpDatastoreRequestHandler.executeRequest(cmResourceAddress, options, topic, includeDescendants, + authorization); + } + + /** + * Executes asynchronous request for group of cm handles to resource data. + * + * @param topic the topic param in query + * @param dataOperationRequest data operation request details for resource data + * @param authorization contents of Authorization header, or null if not present + * @return a map with one entry of request Id for success or status and error when async feature is disabled + */ + public Object executeDataOperationForCmHandles(final String topic, + final DataOperationRequest dataOperationRequest, + final String authorization) { + return ncmpPassthroughResourceRequestHandler.executeAsynchronousRequest(topic, + dataOperationRequest, + authorization); + } + + public Collection queryResourceDataForCmHandle(final String cmHandle, + final String cpsPath, + final Boolean includeDescendants) { + return ncmpCachedResourceRequestHandler.executeRequest(cmHandle, cpsPath, includeDescendants); + } + + /** + * Write resource data for data store pass-through running using dmi for given cm-handle. + * + * @param cmHandleId cm handle identifier + * @param resourceIdentifier resource identifier + * @param operationType required operation type + * @param requestData request body to create resource + * @param dataType content type in body + * @param authorization contents of Authorization header, or null if not present + * @return {@code Object} return data + */ + public Object writeResourceDataPassThroughRunningForCmHandle(final String cmHandleId, + final String resourceIdentifier, + final OperationType operationType, + final String requestData, + final String dataType, + final String authorization) { + return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, + operationType, requestData, dataType, authorization); + } + + + private NcmpDatastoreRequestHandler getNcmpDatastoreRequestHandler(final String datastoreName) { + if (OPERATIONAL.equals(DatastoreType.fromDatastoreName(datastoreName))) { + return ncmpCachedResourceRequestHandler; + } + return ncmpPassthroughResourceRequestHandler; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyInventoryFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyInventoryFacade.java new file mode 100644 index 0000000000..5b439ebcfe --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyInventoryFacade.java @@ -0,0 +1,207 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 highstreet technologies GmbH + * Modifications Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2021-2022 Bell Canada + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl; + +import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCmHandleQueryParameters; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.ParameterizedCmHandleQueryService; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService; +import org.onap.cps.ncmp.api.impl.inventory.CompositeState; +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions; +import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions; +import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; +import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; +import org.onap.cps.ncmp.api.models.DmiPluginRegistration; +import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.spi.model.ModuleDefinition; +import org.onap.cps.spi.model.ModuleReference; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class NetworkCmProxyInventoryFacade { + + private final CmHandleRegistrationService cmHandleRegistrationService; + private final CmHandleQueryService cmHandleQueryService; + private final ParameterizedCmHandleQueryService parameterizedCmHandleQueryService; + private final InventoryPersistence inventoryPersistence; + private final JsonObjectMapper jsonObjectMapper; + + /** + * Registration of Created, Removed, Updated or Upgraded CM Handles. + * + * @param dmiPluginRegistration Dmi Plugin Registration details + * @return dmiPluginRegistrationResponse + */ + + public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( + final DmiPluginRegistration dmiPluginRegistration) { + return cmHandleRegistrationService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration); + } + + /** + * Get all cm handle IDs by DMI plugin identifier. + * + * @param dmiPluginIdentifier DMI plugin identifier + * @return collection of cm handle IDs + */ + public Collection getAllCmHandleIdsByDmiPluginIdentifier(final String dmiPluginIdentifier) { + return cmHandleQueryService.getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifier); + } + + /** + * Get all cm handle IDs by various properties. + * + * @param cmHandleQueryServiceParameters cm handle query parameters + * @return collection of cm handle IDs + */ + public Collection executeParameterizedCmHandleIdSearch( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + validateCmHandleQueryParameters(cmHandleQueryServiceParameters, InventoryQueryConditions.ALL_CONDITION_NAMES); + return parameterizedCmHandleQueryService.queryCmHandleIdsForInventory(cmHandleQueryServiceParameters); + } + + + /** + * Retrieve module references for the given cm handle. + * + * @param cmHandleId cm handle identifier + * @return a collection of modules names and revisions + */ + public Collection getYangResourcesModuleReferences(final String cmHandleId) { + return inventoryPersistence.getYangResourcesModuleReferences(cmHandleId); + } + + /** + * Retrieve module definitions for the given cm handle. + * + * @param cmHandleId cm handle identifier + * @return a collection of module definition (moduleName, revision and yang resource content) + */ + public Collection getModuleDefinitionsByCmHandleId(final String cmHandleId) { + return inventoryPersistence.getModuleDefinitionsByCmHandleId(cmHandleId); + } + + /** + * Get module definitions for the given parameters. + * + * @param cmHandleId cm-handle identifier + * @param moduleName module name + * @param moduleRevision the revision of the module + * @return list of module definitions (module name, revision, yang resource content) + */ + public Collection getModuleDefinitionsByCmHandleAndModule(final String cmHandleId, + final String moduleName, + final String moduleRevision) { + return inventoryPersistence.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, moduleRevision); + } + + /** + * Retrieve cm handles with details for the given query parameters. + * + * @param cmHandleQueryApiParameters cm handle query parameters + * @return cm handles with details + */ + public Collection executeCmHandleSearch( + final CmHandleQueryApiParameters cmHandleQueryApiParameters) { + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( + cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); + validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); + return parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters); + } + + /** + * Retrieve cm handle ids for the given query parameters. + * + * @param cmHandleQueryApiParameters cm handle query parameters + * @return cm handle ids + */ + public Collection executeCmHandleIdSearch(final CmHandleQueryApiParameters cmHandleQueryApiParameters) { + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( + cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); + validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); + return parameterizedCmHandleQueryService.queryCmHandleIds(cmHandleQueryServiceParameters); + } + + /** + * Set the data sync enabled flag, along with the data sync state + * based on the data sync enabled boolean for the cm handle id provided. + * + * @param cmHandleId cm handle id + * @param dataSyncEnabledTargetValue data sync enabled flag + */ + public void setDataSyncEnabled(final String cmHandleId, final Boolean dataSyncEnabledTargetValue) { + cmHandleRegistrationService.setDataSyncEnabled(cmHandleId, dataSyncEnabledTargetValue); + } + + /** + * Retrieve cm handle details for a given cm handle. + * + * @param cmHandleId cm handle identifier + * @return cm handle details + */ + public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleId) { + return YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle( + inventoryPersistence.getYangModelCmHandle(cmHandleId)); + } + + /** + * Get cm handle public properties for a given cm handle id. + * + * @param cmHandleId cm handle identifier + * @return cm handle public properties + */ + public Map getCmHandlePublicProperties(final String cmHandleId) { + final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); + final List yangModelPublicProperties = yangModelCmHandle.getPublicProperties(); + final Map cmHandlePublicProperties = new HashMap<>(); + YangDataConverter.asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties); + return cmHandlePublicProperties; + } + + /** + * Get cm handle composite state for a given cm handle id. + * + * @param cmHandleId cm handle identifier + * @return cm handle state + */ + public CompositeState getCmHandleCompositeState(final String cmHandleId) { + return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState(); + } + + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/ParameterizedCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/ParameterizedCmHandleQueryServiceImpl.java new file mode 100644 index 0000000000..b8418bea33 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/ParameterizedCmHandleQueryServiceImpl.java @@ -0,0 +1,286 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl; + +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; +import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_MODULES; +import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_PROPERTIES; +import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_CPS_PATH; +import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_TRUST_LEVEL; +import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCpsPathConditionProperties; +import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateModuleNameConditionProperties; +import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle; +import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY; +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.cpspath.parser.PathParsingException; +import org.onap.cps.ncmp.api.ParameterizedCmHandleQueryService; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService; +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType; +import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions; +import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.spi.exceptions.DataValidationException; +import org.onap.cps.spi.model.ConditionProperties; +import org.onap.cps.spi.model.DataNode; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHandleQueryService { + + private static final Collection NO_QUERY_TO_EXECUTE = null; + private final CmHandleQueryService cmHandleQueryService; + private final InventoryPersistence inventoryPersistence; + + @Override + public Collection queryCmHandleIds( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + return executeQueries(cmHandleQueryServiceParameters, + this::executeCpsPathQuery, + this::queryCmHandlesByPublicProperties, + this::executeModuleNameQuery, + this::queryCmHandlesByTrustLevel); + } + + @Override + public Collection queryCmHandleIdsForInventory( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + return executeQueries(cmHandleQueryServiceParameters, + this::queryCmHandlesByPublicProperties, + this::queryCmHandlesByPrivateProperties, + this::queryCmHandlesByDmiPlugin); + } + + @Override + public Collection queryCmHandles( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + + if (cmHandleQueryServiceParameters.getCmHandleQueryParameters().isEmpty()) { + return getAllCmHandles(); + } + + final Collection cmHandleIds = queryCmHandleIds(cmHandleQueryServiceParameters); + + return getNcmpServiceCmHandles(cmHandleIds); + } + + @Override + public Collection getAllCmHandles() { + final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT).iterator().next(); + return dataNode.getChildDataNodes().stream().map(this::createNcmpServiceCmHandle).collect(Collectors.toSet()); + } + + private Collection queryCmHandlesByDmiPlugin( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + final Map dmiPropertyQueryPairs = + getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), + InventoryQueryConditions.CM_HANDLE_WITH_DMI_PLUGIN.getName()); + if (dmiPropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + + final String dmiPluginIdentifierValue = dmiPropertyQueryPairs + .get(PropertyType.DMI_PLUGIN.getYangContainerName()); + + return cmHandleQueryService.getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifierValue); + } + + private Collection queryCmHandlesByPrivateProperties( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + + final Map privatePropertyQueryPairs = + getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), + InventoryQueryConditions.HAS_ALL_ADDITIONAL_PROPERTIES.getName()); + + if (privatePropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return cmHandleQueryService.queryCmHandleAdditionalProperties(privatePropertyQueryPairs); + } + + private Collection queryCmHandlesByPublicProperties( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + + final Map publicPropertyQueryPairs = + getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), + HAS_ALL_PROPERTIES.getConditionName()); + + if (publicPropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return cmHandleQueryService.queryCmHandlePublicProperties(publicPropertyQueryPairs); + } + + private Collection queryCmHandlesByTrustLevel(final CmHandleQueryServiceParameters + cmHandleQueryServiceParameters) { + + final Map trustLevelPropertyQueryPairs = + getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), + WITH_TRUST_LEVEL.getConditionName()); + + if (trustLevelPropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return cmHandleQueryService.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs); + } + + private Collection executeModuleNameQuery( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + final Collection moduleNamesForQuery = + getModuleNamesForQuery(cmHandleQueryServiceParameters.getCmHandleQueryParameters()); + if (moduleNamesForQuery.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return inventoryPersistence.getCmHandleIdsWithGivenModules(moduleNamesForQuery); + } + + private Collection executeCpsPathQuery( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + final Map cpsPathCondition + = getCpsPathCondition(cmHandleQueryServiceParameters.getCmHandleQueryParameters()); + if (!validateCpsPathConditionProperties(cpsPathCondition)) { + return Collections.emptySet(); + } + final Collection cpsPathQueryResult; + if (cpsPathCondition.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + try { + cpsPathQueryResult = collectCmHandleIdsFromDataNodes( + cmHandleQueryService.queryCmHandleAncestorsByCpsPath( + cpsPathCondition.get("cpsPath"), OMIT_DESCENDANTS)); + } catch (final PathParsingException pathParsingException) { + throw new DataValidationException(pathParsingException.getMessage(), pathParsingException.getDetails(), + pathParsingException); + } + return cpsPathQueryResult; + } + + private Collection getModuleNamesForQuery(final List conditionProperties) { + final List result = new ArrayList<>(); + getConditions(conditionProperties, HAS_ALL_MODULES.getConditionName()).forEach( + conditionProperty -> { + validateModuleNameConditionProperties(conditionProperty); + result.add(conditionProperty.get("moduleName")); + }); + return result; + } + + private Map getCpsPathCondition(final List conditionProperties) { + final Map result = new HashMap<>(); + getConditions(conditionProperties, WITH_CPS_PATH.getConditionName()).forEach(result::putAll); + return result; + } + + private Map getPropertyPairs(final List conditionProperties, + final String queryProperty) { + final Map result = new HashMap<>(); + getConditions(conditionProperties, queryProperty).forEach(result::putAll); + return result; + } + + private List> getConditions(final List conditionProperties, + final String name) { + for (final ConditionProperties conditionProperty : conditionProperties) { + if (conditionProperty.getConditionName().equals(name)) { + return conditionProperty.getConditionParameters(); + } + } + return Collections.emptyList(); + } + + private Collection getAllCmHandleIds() { + final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT, DIRECT_CHILDREN_ONLY) + .iterator().next(); + return collectCmHandleIdsFromDataNodes(dataNode.getChildDataNodes()); + } + + private Collection getNcmpServiceCmHandles(final Collection cmHandleIds) { + final Collection yangModelcmHandles + = inventoryPersistence.getYangModelCmHandles(cmHandleIds); + + final Collection ncmpServiceCmHandles = new ArrayList<>(yangModelcmHandles.size()); + + yangModelcmHandles.forEach(yangModelcmHandle -> + ncmpServiceCmHandles.add(YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle(yangModelcmHandle)) + ); + return ncmpServiceCmHandles; + } + + private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) { + return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter.convertCmHandleToYangModel(dataNode)); + } + + private Collection executeQueries(final CmHandleQueryServiceParameters cmHandleQueryServiceParameters, + final Function>... + queryFunctions) { + if (cmHandleQueryServiceParameters.getCmHandleQueryParameters().isEmpty()) { + return getAllCmHandleIds(); + } + Collection combinedQueryResult = NO_QUERY_TO_EXECUTE; + for (final Function> queryFunction : queryFunctions) { + final Collection queryResult = queryFunction.apply(cmHandleQueryServiceParameters); + if (noEntriesFoundCanStopQuerying(queryResult)) { + return Collections.emptySet(); + } + combinedQueryResult = combineCmHandleQueryResults(combinedQueryResult, queryResult); + } + return combinedQueryResult; + } + + private boolean noEntriesFoundCanStopQuerying(final Collection queryResult) { + return queryResult != NO_QUERY_TO_EXECUTE && queryResult.isEmpty(); + } + + private Collection combineCmHandleQueryResults(final Collection firstQuery, + final Collection secondQuery) { + if (firstQuery == NO_QUERY_TO_EXECUTE && secondQuery == NO_QUERY_TO_EXECUTE) { + return NO_QUERY_TO_EXECUTE; + } else if (firstQuery == NO_QUERY_TO_EXECUTE) { + return secondQuery; + } else if (secondQuery == NO_QUERY_TO_EXECUTE) { + return firstQuery; + } else { + firstQuery.retainAll(secondQuery); + return firstQuery; + } + } + + private Collection collectCmHandleIdsFromDataNodes(final Collection dataNodes) { + return dataNodes.stream().map(dataNode -> (String) dataNode.getLeaves().get("id")).collect(Collectors.toSet()); + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java index 5811cf97da..d4c5d16a4c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java @@ -71,7 +71,7 @@ public class DmiRestClient { private final WebClient healthChecksWebClient; /** - * Sends a POST operation to the DMI with a JSON body containing module references. + * Sends a synchronous (blocking) POST operation to the DMI with a JSON body containing module references. * * @param requiredDmiService Determines if the required service is for a data or model operation. * @param dmiUrl The DMI resource URL. @@ -81,17 +81,18 @@ public class DmiRestClient { * @return ResponseEntity containing the response from the DMI. * @throws DmiClientRequestException If there is an error during the DMI request. */ - public ResponseEntity postOperationWithJsonData(final RequiredDmiService requiredDmiService, - final String dmiUrl, - final String requestBodyAsJsonString, - final OperationType operationType, - final String authorization) { - try { - return postOperationWithJsonDataAsync(requiredDmiService, dmiUrl, requestBodyAsJsonString, operationType, - authorization).block(); - } catch (final HttpServerErrorException e) { - throw handleDmiClientException(e, operationType.getOperationName()); - } + public ResponseEntity synchronousPostOperationWithJsonData(final RequiredDmiService requiredDmiService, + final String dmiUrl, + final String requestBodyAsJsonString, + final OperationType operationType, + final String authorization) { + final Mono> responseEntityMono = + asynchronousPostOperationWithJsonData(requiredDmiService, + dmiUrl, + requestBodyAsJsonString, + operationType, + authorization); + return responseEntityMono.block(); } /** @@ -105,11 +106,12 @@ public class DmiRestClient { * @param authorization The authorization token to be added to the request headers. * @return A Mono emitting the response entity containing the server's response. */ - public Mono> postOperationWithJsonDataAsync(final RequiredDmiService requiredDmiService, - final String dmiUrl, - final String requestBodyAsJsonString, - final OperationType operationType, - final String authorization) { + public Mono> asynchronousPostOperationWithJsonData( + final RequiredDmiService requiredDmiService, + final String dmiUrl, + final String requestBodyAsJsonString, + final OperationType operationType, + final String authorization) { final WebClient webClient = getWebClient(requiredDmiService); return webClient.post() .uri(toUri(dmiUrl)) diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java deleted file mode 100644 index 81467dbb3e..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.inventory; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import org.onap.cps.spi.FetchDescendantsOption; -import org.onap.cps.spi.model.DataNode; - -public interface CmHandleQueries { - - /** - * Query CmHandles based on additional (private) properties. - * - * @param additionalPropertyQueryPairs private properties for query - * @return Ids of CmHandles which have these private properties - */ - Collection queryCmHandleAdditionalProperties(Map additionalPropertyQueryPairs); - - /** - * Query CmHandles based on public properties. - * - * @param publicPropertyQueryPairs public properties for query - * @return CmHandles which have these public properties - */ - Collection queryCmHandlePublicProperties(Map publicPropertyQueryPairs); - - /** - * Query CmHandles based on Trust Level. - * - * @param trustLevelPropertyQueryPairs trust level properties for query - * @return CmHandles which have desired trust level - */ - Collection queryCmHandlesByTrustLevel(Map trustLevelPropertyQueryPairs); - - /** - * Method which returns cm handles by the cm handles state. - * - * @param cmHandleState cm handle state - * @return a list of cm handles - */ - List queryCmHandlesByState(CmHandleState cmHandleState); - - /** - * Method to return data nodes with ancestor representing the cm handles. - * - * @param cpsPath cps path for which the cmHandle is requested - * @return a list of data nodes representing the cm handles. - */ - List queryCmHandleAncestorsByCpsPath(String cpsPath, - FetchDescendantsOption fetchDescendantsOption); - - /** - * Method to return data nodes representing the cm handles. - * - * @param cpsPath cps path for which the cmHandle is requested - * @return a list of data nodes representing the cm handles. - */ - List queryNcmpRegistryByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption); - - /** - * Method to check the state of a cm handle with given id. - * - * @param cmHandleId cm handle id - * @param requiredCmHandleState the required state of the cm handle - * @return a boolean, true if the state is equal to the required state - */ - boolean cmHandleHasState(String cmHandleId, CmHandleState requiredCmHandleState); - - /** - * Method which returns cm handles by the operational sync state of cm handle. - * - * @param dataStoreSyncState sync state - * @return a list of cm handles - */ - List queryCmHandlesByOperationalSyncState(DataStoreSyncState dataStoreSyncState); - - /** - * Get all cm handles ids by DMI plugin identifier. - * - * @param dmiPluginIdentifier DMI plugin identifier - * @return collection of cm handles - */ - Collection getCmHandleIdsByDmiPluginIdentifier(String dmiPluginIdentifier); -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java deleted file mode 100644 index 6cffb4d274..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 Nordix Foundation - * Modifications Copyright (C) 2023 TechMahindra Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.inventory; - -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; -import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS; -import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; -import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; -import org.onap.cps.spi.CpsDataPersistenceService; -import org.onap.cps.spi.FetchDescendantsOption; -import org.onap.cps.spi.model.DataNode; -import org.onap.cps.spi.utils.CpsValidator; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -@RequiredArgsConstructor -@Component -public class CmHandleQueriesImpl implements CmHandleQueries { - - private static final String DESCENDANT_PATH = "//"; - private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles"; - private final CpsDataPersistenceService cpsDataPersistenceService; - - @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN) - private final Map trustLevelPerDmiPlugin; - - @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_CM_HANDLE) - private final Map trustLevelPerCmHandle; - - private final CpsValidator cpsValidator; - - @Override - public Collection queryCmHandleAdditionalProperties(final Map privatePropertyQueryPairs) { - return queryCmHandleAnyProperties(privatePropertyQueryPairs, PropertyType.ADDITIONAL); - } - - @Override - public Collection queryCmHandlePublicProperties(final Map publicPropertyQueryPairs) { - return queryCmHandleAnyProperties(publicPropertyQueryPairs, PropertyType.PUBLIC); - } - - @Override - public Collection queryCmHandlesByTrustLevel(final Map trustLevelPropertyQueryPairs) { - final String trustLevelProperty = trustLevelPropertyQueryPairs.values().iterator().next(); - final TrustLevel targetTrustLevel = TrustLevel.valueOf(trustLevelProperty); - - return getCmHandleIdsByTrustLevel(targetTrustLevel); - } - - @Override - public List queryCmHandlesByState(final CmHandleState cmHandleState) { - return queryCmHandleAncestorsByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]", - INCLUDE_ALL_DESCENDANTS); - } - - @Override - public List queryNcmpRegistryByCpsPath(final String cpsPath, - final FetchDescendantsOption fetchDescendantsOption) { - return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - cpsPath, fetchDescendantsOption); - } - - @Override - public List queryCmHandleAncestorsByCpsPath(final String cpsPath, - final FetchDescendantsOption fetchDescendantsOption) { - return queryNcmpRegistryByCpsPath(cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption); - } - - @Override - public boolean cmHandleHasState(final String cmHandleId, final CmHandleState requiredCmHandleState) { - final DataNode stateDataNode = getCmHandleState(cmHandleId); - final String cmHandleStateAsString = (String) stateDataNode.getLeaves().get("cm-handle-state"); - return CmHandleState.valueOf(cmHandleStateAsString).equals(requiredCmHandleState); - } - - @Override - public List queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) { - return queryCmHandleAncestorsByCpsPath("//state/datastores" + "/operational[@sync-state=\"" - + dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS); - } - - @Override - public Collection getCmHandleIdsByDmiPluginIdentifier(final String dmiPluginIdentifier) { - final Collection cmHandleIds = new HashSet<>(); - for (final ModelledDmiServiceLeaves modelledDmiServiceLeaf : ModelledDmiServiceLeaves.values()) { - for (final DataNode cmHandleAsDataNode: getCmHandlesByDmiPluginIdentifierAndDmiProperty( - dmiPluginIdentifier, - modelledDmiServiceLeaf.getLeafName())) { - cmHandleIds.add(cmHandleAsDataNode.getLeaves().get("id").toString()); - } - } - return cmHandleIds; - } - - private Collection getCmHandleIdsByTrustLevel(final TrustLevel targetTrustLevel) { - final Collection selectedCmHandleIds = new HashSet<>(); - - for (final Map.Entry mapEntry : trustLevelPerDmiPlugin.entrySet()) { - final String dmiPluginIdentifier = mapEntry.getKey(); - final TrustLevel dmiTrustLevel = mapEntry.getValue(); - final Collection candidateCmHandleIds = getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifier); - for (final String candidateCmHandleId : candidateCmHandleIds) { - final TrustLevel candidateCmHandleTrustLevel = trustLevelPerCmHandle.get(candidateCmHandleId); - final TrustLevel effectiveTrustlevel = - candidateCmHandleTrustLevel.getEffectiveTrustLevel(dmiTrustLevel); - if (targetTrustLevel.equals(effectiveTrustlevel)) { - selectedCmHandleIds.add(candidateCmHandleId); - } - } - } - - return selectedCmHandleIds; - } - - private Collection collectCmHandleIdsFromDataNodes(final Collection dataNodes) { - return dataNodes.stream().map(dataNode -> (String) dataNode.getLeaves().get("id")).collect(Collectors.toSet()); - } - - private Collection queryCmHandleAnyProperties( - final Map propertyQueryPairs, - final PropertyType propertyType) { - if (propertyQueryPairs.isEmpty()) { - return Collections.emptySet(); - } - Collection cmHandleIds = null; - for (final Map.Entry publicPropertyQueryPair : propertyQueryPairs.entrySet()) { - final String cpsPath = DESCENDANT_PATH + propertyType.getYangContainerName() + "[@name=\"" - + publicPropertyQueryPair.getKey() - + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]"; - - final Collection dataNodes = queryCmHandleAncestorsByCpsPath(cpsPath, - OMIT_DESCENDANTS); - if (cmHandleIds == null) { - cmHandleIds = collectCmHandleIdsFromDataNodes(dataNodes); - } else { - final Collection cmHandleIdsToRetain = collectCmHandleIdsFromDataNodes(dataNodes); - cmHandleIds.retainAll(cmHandleIdsToRetain); - } - if (cmHandleIds.isEmpty()) { - break; - } - } - return cmHandleIds; - } - - private List getCmHandlesByDmiPluginIdentifierAndDmiProperty(final String dmiPluginIdentifier, - final String dmiProperty) { - return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@" + dmiProperty + "='" + dmiPluginIdentifier + "']", - OMIT_DESCENDANTS); - } - - private DataNode getCmHandleState(final String cmHandleId) { - cpsValidator.validateNameCharacters(cmHandleId); - final String xpath = NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']/state"; - return cpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - xpath, OMIT_DESCENDANTS).iterator().next(); - } -} - - diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryService.java new file mode 100644 index 0000000000..970c36bb63 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryService.java @@ -0,0 +1,105 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.inventory; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.DataNode; + +public interface CmHandleQueryService { + + /** + * Query Cm Handles based on additional (private) properties. + * + * @param additionalPropertyQueryPairs private properties for query + * @return Ids of Cm Handles which have these private properties + */ + Collection queryCmHandleAdditionalProperties(Map additionalPropertyQueryPairs); + + /** + * Query Cm Handles based on public properties. + * + * @param publicPropertyQueryPairs public properties for query + * @return CmHandles which have these public properties + */ + Collection queryCmHandlePublicProperties(Map publicPropertyQueryPairs); + + /** + * Query Cm Handles based on Trust Level. + * + * @param trustLevelPropertyQueryPairs trust level properties for query + * @return Ids of Cm Handles which have desired trust level + */ + Collection queryCmHandlesByTrustLevel(Map trustLevelPropertyQueryPairs); + + /** + * Method which returns cm handles by the cm handles state. + * + * @param cmHandleState cm handle state + * @return a list of data nodes representing the cm handles. + */ + List queryCmHandlesByState(CmHandleState cmHandleState); + + /** + * Method to return data nodes with ancestor representing the cm handles. + * + * @param cpsPath cps path for which the cmHandle is requested + * @return a list of data nodes representing the cm handles. + */ + List queryCmHandleAncestorsByCpsPath(String cpsPath, + FetchDescendantsOption fetchDescendantsOption); + + /** + * Method to return data nodes representing the cm handles. + * + * @param cpsPath cps path for which the cmHandle is requested + * @return a list of data nodes representing the cm handles. + */ + List queryNcmpRegistryByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption); + + /** + * Method to check the state of a cm handle with given id. + * + * @param cmHandleId cm handle id + * @param requiredCmHandleState the required state of the cm handle + * @return a boolean, true if the state is equal to the required state + */ + boolean cmHandleHasState(String cmHandleId, CmHandleState requiredCmHandleState); + + /** + * Method which returns cm handles by the operational sync state of cm handle. + * + * @param dataStoreSyncState sync state + * @return a list of data nodes representing the cm handles. + */ + List queryCmHandlesByOperationalSyncState(DataStoreSyncState dataStoreSyncState); + + /** + * Get all cm handles ids by DMI plugin identifier. + * + * @param dmiPluginIdentifier DMI plugin identifier + * @return collection of cm handle ids + */ + Collection getCmHandleIdsByDmiPluginIdentifier(String dmiPluginIdentifier); + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryServiceImpl.java new file mode 100644 index 0000000000..4350cc6fdf --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryServiceImpl.java @@ -0,0 +1,192 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.inventory; + +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; +import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS; +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; +import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType; +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; +import org.onap.cps.spi.CpsDataPersistenceService; +import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.DataNode; +import org.onap.cps.spi.utils.CpsValidator; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class CmHandleQueryServiceImpl implements CmHandleQueryService { + + private static final String DESCENDANT_PATH = "//"; + private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles"; + private final CpsDataPersistenceService cpsDataPersistenceService; + + @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN) + private final Map trustLevelPerDmiPlugin; + + @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_CM_HANDLE) + private final Map trustLevelPerCmHandle; + + private final CpsValidator cpsValidator; + + @Override + public Collection queryCmHandleAdditionalProperties(final Map privatePropertyQueryPairs) { + return queryCmHandleAnyProperties(privatePropertyQueryPairs, PropertyType.ADDITIONAL); + } + + @Override + public Collection queryCmHandlePublicProperties(final Map publicPropertyQueryPairs) { + return queryCmHandleAnyProperties(publicPropertyQueryPairs, PropertyType.PUBLIC); + } + + @Override + public Collection queryCmHandlesByTrustLevel(final Map trustLevelPropertyQueryPairs) { + final String trustLevelProperty = trustLevelPropertyQueryPairs.values().iterator().next(); + final TrustLevel targetTrustLevel = TrustLevel.valueOf(trustLevelProperty); + + return getCmHandleIdsByTrustLevel(targetTrustLevel); + } + + @Override + public List queryCmHandlesByState(final CmHandleState cmHandleState) { + return queryCmHandleAncestorsByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]", + INCLUDE_ALL_DESCENDANTS); + } + + @Override + public List queryNcmpRegistryByCpsPath(final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption) { + return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + cpsPath, fetchDescendantsOption); + } + + @Override + public List queryCmHandleAncestorsByCpsPath(final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption) { + return queryNcmpRegistryByCpsPath(cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption); + } + + @Override + public boolean cmHandleHasState(final String cmHandleId, final CmHandleState requiredCmHandleState) { + final DataNode stateDataNode = getCmHandleState(cmHandleId); + final String cmHandleStateAsString = (String) stateDataNode.getLeaves().get("cm-handle-state"); + return CmHandleState.valueOf(cmHandleStateAsString).equals(requiredCmHandleState); + } + + @Override + public List queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) { + return queryCmHandleAncestorsByCpsPath("//state/datastores" + "/operational[@sync-state=\"" + + dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS); + } + + @Override + public Collection getCmHandleIdsByDmiPluginIdentifier(final String dmiPluginIdentifier) { + final Collection cmHandleIds = new HashSet<>(); + for (final ModelledDmiServiceLeaves modelledDmiServiceLeaf : ModelledDmiServiceLeaves.values()) { + for (final DataNode cmHandleAsDataNode: getCmHandlesByDmiPluginIdentifierAndDmiProperty( + dmiPluginIdentifier, + modelledDmiServiceLeaf.getLeafName())) { + cmHandleIds.add(cmHandleAsDataNode.getLeaves().get("id").toString()); + } + } + return cmHandleIds; + } + + private Collection getCmHandleIdsByTrustLevel(final TrustLevel targetTrustLevel) { + final Collection selectedCmHandleIds = new HashSet<>(); + + for (final Map.Entry mapEntry : trustLevelPerDmiPlugin.entrySet()) { + final String dmiPluginIdentifier = mapEntry.getKey(); + final TrustLevel dmiTrustLevel = mapEntry.getValue(); + final Collection candidateCmHandleIds = getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifier); + for (final String candidateCmHandleId : candidateCmHandleIds) { + final TrustLevel candidateCmHandleTrustLevel = trustLevelPerCmHandle.get(candidateCmHandleId); + final TrustLevel effectiveTrustlevel = + candidateCmHandleTrustLevel.getEffectiveTrustLevel(dmiTrustLevel); + if (targetTrustLevel.equals(effectiveTrustlevel)) { + selectedCmHandleIds.add(candidateCmHandleId); + } + } + } + + return selectedCmHandleIds; + } + + private Collection collectCmHandleIdsFromDataNodes(final Collection dataNodes) { + return dataNodes.stream().map(dataNode -> (String) dataNode.getLeaves().get("id")).collect(Collectors.toSet()); + } + + private Collection queryCmHandleAnyProperties( + final Map propertyQueryPairs, + final PropertyType propertyType) { + if (propertyQueryPairs.isEmpty()) { + return Collections.emptySet(); + } + Collection cmHandleIds = null; + for (final Map.Entry publicPropertyQueryPair : propertyQueryPairs.entrySet()) { + final String cpsPath = DESCENDANT_PATH + propertyType.getYangContainerName() + "[@name=\"" + + publicPropertyQueryPair.getKey() + + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]"; + + final Collection dataNodes = queryCmHandleAncestorsByCpsPath(cpsPath, + OMIT_DESCENDANTS); + if (cmHandleIds == null) { + cmHandleIds = collectCmHandleIdsFromDataNodes(dataNodes); + } else { + final Collection cmHandleIdsToRetain = collectCmHandleIdsFromDataNodes(dataNodes); + cmHandleIds.retainAll(cmHandleIdsToRetain); + } + if (cmHandleIds.isEmpty()) { + break; + } + } + return cmHandleIds; + } + + private List getCmHandlesByDmiPluginIdentifierAndDmiProperty(final String dmiPluginIdentifier, + final String dmiProperty) { + return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@" + dmiProperty + "='" + dmiPluginIdentifier + "']", + OMIT_DESCENDANTS); + } + + private DataNode getCmHandleState(final String cmHandleId) { + cpsValidator.validateNameCharacters(cmHandleId); + final String xpath = NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']/state"; + return cpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + xpath, OMIT_DESCENDANTS).iterator().next(); + } +} + + diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java index c4cab31ab3..55fe35de2f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java @@ -57,7 +57,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv private final CpsModuleService cpsModuleService; private final CpsAnchorService cpsAnchorService; private final CpsValidator cpsValidator; - private final CmHandleQueries cmHandleQueries; + private final CmHandleQueryService cmHandleQueryService; /** * initialize an inventory persistence object. @@ -70,12 +70,13 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv */ public InventoryPersistenceImpl(final JsonObjectMapper jsonObjectMapper, final CpsDataService cpsDataService, final CpsModuleService cpsModuleService, final CpsValidator cpsValidator, - final CpsAnchorService cpsAnchorService, final CmHandleQueries cmHandleQueries) { + final CpsAnchorService cpsAnchorService, + final CmHandleQueryService cmHandleQueryService) { super(jsonObjectMapper, cpsDataService, cpsModuleService, cpsValidator); this.cpsModuleService = cpsModuleService; this.cpsAnchorService = cpsAnchorService; this.cpsValidator = cpsValidator; - this.cmHandleQueries = cmHandleQueries; + this.cmHandleQueryService = cmHandleQueryService; } @@ -170,7 +171,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv @Override public DataNode getCmHandleDataNodeByAlternateId(final String alternateId) { final String cpsPathForCmHandleByAlternateId = getCpsPathForCmHandleByAlternateId(alternateId); - final Collection dataNodes = cmHandleQueries + final Collection dataNodes = cmHandleQueryService .queryNcmpRegistryByCpsPath(cpsPathForCmHandleByAlternateId, OMIT_DESCENDANTS); if (dataNodes.isEmpty()) { throw new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java index 794ca5b1b6..e00a49f57a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java @@ -21,8 +21,6 @@ package org.onap.cps.ncmp.api.impl.inventory.sync; -import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL; - import com.fasterxml.jackson.databind.JsonNode; import java.time.Duration; import java.time.OffsetDateTime; @@ -38,7 +36,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService; import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; import org.onap.cps.ncmp.api.impl.inventory.CompositeState; import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState; @@ -57,7 +55,7 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class ModuleOperationsUtils { - private final CmHandleQueries cmHandleQueries; + private final CmHandleQueryService cmHandleQueryService; private final DmiDataOperations dmiDataOperations; private final JsonObjectMapper jsonObjectMapper; private static final String RETRY_ATTEMPT_KEY = "attempt"; @@ -78,7 +76,8 @@ public class ModuleOperationsUtils { * @return cm handles (data nodes) in ADVISED state (empty list if none found) */ public List getAdvisedCmHandles() { - final List advisedCmHandlesAsDataNodes = cmHandleQueries.queryCmHandlesByState(CmHandleState.ADVISED); + final List advisedCmHandlesAsDataNodes = + cmHandleQueryService.queryCmHandlesByState(CmHandleState.ADVISED); log.debug("Total number of fetched advised cm handle(s) is (are) {}", advisedCmHandlesAsDataNodes.size()); return advisedCmHandlesAsDataNodes; } @@ -91,13 +90,13 @@ public class ModuleOperationsUtils { * return empty list if not found */ public List getUnsynchronizedReadyCmHandles() { - final List unsynchronizedCmHandles = cmHandleQueries + final List unsynchronizedCmHandles = cmHandleQueryService .queryCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED); final List yangModelCmHandles = new ArrayList<>(); for (final DataNode unsynchronizedCmHandle : unsynchronizedCmHandles) { final String cmHandleId = unsynchronizedCmHandle.getLeaves().get("id").toString(); - if (cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY)) { + if (cmHandleQueryService.cmHandleHasState(cmHandleId, CmHandleState.READY)) { yangModelCmHandles.addAll(convertCmHandlesDataNodesToYangModelCmHandles( Collections.singletonList(unsynchronizedCmHandle))); } @@ -113,7 +112,7 @@ public class ModuleOperationsUtils { */ public List getCmHandlesThatFailedModelSyncOrUpgrade() { final List lockedCmHandlesAsDataNodeList - = cmHandleQueries.queryCmHandleAncestorsByCpsPath(CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE, + = cmHandleQueryService.queryCmHandleAncestorsByCpsPath(CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); return convertCmHandlesDataNodesToYangModelCmHandles(lockedCmHandlesAsDataNodeList); } @@ -198,16 +197,14 @@ public class ModuleOperationsUtils { } /** - * Get the Resourece Data from Node through DMI Passthrough service. + * Get the Resource Data from Node through DMI Passthrough service. * * @param cmHandleId cm handle id * @return optional string containing the resource data */ public String getResourceData(final String cmHandleId) { - final ResponseEntity resourceDataResponseEntity = dmiDataOperations.getResourceDataFromDmi( - PASSTHROUGH_OPERATIONAL.getDatastoreName(), - cmHandleId, - UUID.randomUUID().toString()); + final ResponseEntity resourceDataResponseEntity = dmiDataOperations.getAllResourceDataFromDmi( + cmHandleId, UUID.randomUUID().toString()); if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) { return getFirstResource(resourceDataResponseEntity.getBody()); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java index 45156ce88e..a5f0ba017f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java @@ -38,7 +38,7 @@ import org.apache.commons.lang3.StringUtils; import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsModuleService; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService; import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations; import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; @@ -59,7 +59,7 @@ public class ModuleSyncService { private final DmiModelOperations dmiModelOperations; private final CpsModuleService cpsModuleService; - private final CmHandleQueries cmHandleQueries; + private final CmHandleQueryService cmHandleQueryService; private final CpsDataService cpsDataService; private final CpsAnchorService cpsAnchorService; private final JsonObjectMapper jsonObjectMapper; @@ -136,7 +136,7 @@ public class ModuleSyncService { return null; } final String escapedModuleSetTag = moduleSetTag.replace("'", "''"); - final List dataNodes = cmHandleQueries.queryNcmpRegistryByCpsPath( + final List dataNodes = cmHandleQueryService.queryNcmpRegistryByCpsPath( NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@module-set-tag='" + escapedModuleSetTag + "']", FetchDescendantsOption.DIRECT_CHILDREN_ONLY); return dataNodes.stream().map(YangDataConverter::convertCmHandleToYangModel) 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 3db84556e9..7359518dc9 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 @@ -21,6 +21,7 @@ package org.onap.cps.ncmp.api.impl.operations; +import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL; import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING; import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ; import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA; @@ -69,49 +70,48 @@ public class DmiDataOperations { * This method fetches the resource data from the operational data store for a given CM handle * identifier on the specified resource using the DMI client. * - * @param cmResourceAddress Target datastore, CM handle, and resource identifier. - * @param optionsParamInQuery Options query string. - * @param topicParamInQuery Topic name for triggering asynchronous responses. - * @param requestId Request ID for asynchronous responses. - * @param authorization Contents of the Authorization header, or null if not present. + * @param cmResourceAddress Target datastore, CM handle, and resource identifier. + * @param options Options query string. + * @param topic Topic name for triggering asynchronous responses. + * @param requestId Request ID for asynchronous responses. + * @param authorization Contents of the Authorization header, or null if not present. * @return {@code Mono>} A reactive type representing the response entity. */ @Timed(value = "cps.ncmp.dmi.get", description = "Time taken to fetch the resource data from operational data store for given cm handle " + "identifier on given resource using dmi client") public Mono> getResourceDataFromDmi(final CmResourceAddress cmResourceAddress, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final String authorization) { + final String options, + final String topic, + final String requestId, + final String authorization) { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.cmHandleId()); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); final String dmiUrl = getDmiResourceDataUrl(cmResourceAddress.datastoreName(), yangModelCmHandle, - cmResourceAddress.resourceIdentifier(), optionsParamInQuery, topicParamInQuery); - return dmiRestClient.postOperationWithJsonDataAsync(DATA, dmiUrl, jsonRequestBody, READ, authorization); + cmResourceAddress.resourceIdentifier(), options, topic); + return dmiRestClient.asynchronousPostOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, READ, authorization); } /** * This method fetches all the resource data from operational data store for given cm handle * identifier using dmi client. + * Note: this method is only used for DataSync * - * @param datastoreName data store name * @param cmHandleId network resource identifier * @param requestId requestId for async responses * @return {@code ResponseEntity} response entity */ - public ResponseEntity getResourceDataFromDmi(final String datastoreName, - final String cmHandleId, - final String requestId) { + public ResponseEntity getAllResourceDataFromDmi(final String cmHandleId, final String requestId) { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); - final String dmiUrl = getDmiResourceDataUrl(datastoreName, yangModelCmHandle, "/", null, null); - return dmiRestClient.postOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, READ, null); + final String dmiUrl = + getDmiResourceDataUrl(PASSTHROUGH_OPERATIONAL.getDatastoreName(), yangModelCmHandle, "/", null, null); + return dmiRestClient.synchronousPostOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, READ, null); } /** @@ -168,7 +168,8 @@ public class DmiDataOperations { yangModelCmHandle); final String dmiUrl = getDmiResourceDataUrl(PASSTHROUGH_RUNNING.getDatastoreName(), yangModelCmHandle, resourceId, null, null); - return dmiRestClient.postOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, operationType, authorization); + return dmiRestClient.synchronousPostOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, + operationType, authorization); } private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { @@ -256,7 +257,7 @@ public class DmiDataOperations { final String authorization) { final String dmiDataOperationRequestAsJsonString = createDmiDataOperationRequestAsJsonString(dmiDataOperationRequestBodies); - return dmiRestClient.postOperationWithJsonDataAsync(DATA, dmiUrl, dmiDataOperationRequestAsJsonString, + return dmiRestClient.asynchronousPostOperationWithJsonData(DATA, dmiUrl, dmiDataOperationRequestAsJsonString, READ, authorization) .then() .onErrorResume(DmiClientRequestException.class, dmiClientRequestException -> { @@ -293,4 +294,4 @@ public class DmiDataOperations { ResourceDataOperationRequestUtils.publishErrorMessageToClientTopic(topicName, requestId, cmHandleIdsPerResponseCodesPerOperation); } -} \ No newline at end of file +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java index 77dfcb7a20..82dccc66da 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java @@ -21,6 +21,7 @@ package org.onap.cps.ncmp.api.impl.operations; +import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ; import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.MODEL; import com.google.gson.JsonArray; @@ -111,7 +112,7 @@ public class DmiModelOperations { .variablePathSegment("cmHandleId", cmHandle) .variablePathSegment("resourceName", resourceName) .build(dmiServiceName, dmiProperties.getDmiBasePath()); - return dmiRestClient.postOperationWithJsonData(MODEL, dmiUrl, jsonRequestBody, OperationType.READ, null); + return dmiRestClient.synchronousPostOperationWithJsonData(MODEL, dmiUrl, jsonRequestBody, READ, null); } private static String getRequestBodyToFetchYangResources(final Collection newModuleReferences, diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java index d6d6fd6bc1..1022512a6c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java @@ -24,9 +24,9 @@ import java.util.Collection; import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService; import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager; import org.springframework.beans.factory.annotation.Qualifier; @@ -39,7 +39,7 @@ import org.springframework.stereotype.Service; public class DmiPluginWatchDog { private final DmiRestClient dmiRestClient; - private final NetworkCmProxyDataService networkCmProxyDataService; + private final CmHandleQueryService cmHandleQueryService; private final TrustLevelManager trustLevelManager; @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN) @@ -68,7 +68,7 @@ public class DmiPluginWatchDog { log.debug("The Dmi Plugin: {} has already the same trust level: {}", dmiServiceName, newDmiTrustLevel); } else { final Collection cmHandleIds = - networkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier(dmiServiceName); + cmHandleQueryService.getCmHandleIdsByDmiPluginIdentifier(dmiServiceName); trustLevelManager.handleUpdateOfDmiTrustLevel(dmiServiceName, cmHandleIds, newDmiTrustLevel); } }); diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServicePropertyHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServicePropertyHandlerSpec.groovy new file mode 100644 index 0000000000..5eac44b8f2 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServicePropertyHandlerSpec.groovy @@ -0,0 +1,296 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * Modifications Copyright (C) 2022 Bell Canada + * Modifications Copyright (C) 2024 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl + +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.read.ListAppender +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.api.CpsDataService +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence +import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle +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.ContentType +import org.onap.cps.utils.JsonObjectMapper +import org.slf4j.LoggerFactory +import spock.lang.Specification + +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR +import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status + +class CmHandleRegistrationServicePropertyHandlerSpec extends Specification { + + def mockInventoryPersistence = Mock(InventoryPersistence) + def mockCpsDataService = Mock(CpsDataService) + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + def mockAlternateIdChecker = Mock(AlternateIdChecker) + + def objectUnderTest = new CmHandleRegistrationServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockAlternateIdChecker) + def logger = Spy(ListAppender) + + void setup() { + def setupLogger = ((Logger) LoggerFactory.getLogger(CmHandleRegistrationServicePropertyHandler.class)) + setupLogger.addAppender(logger) + setupLogger.setLevel(Level.DEBUG) + logger.start() + // Always accept all alternate IDs + mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> [] + } + + void cleanup() { + ((Logger) LoggerFactory.getLogger(CmHandleRegistrationServicePropertyHandler.class)).detachAndStopAllAppenders() + } + + def static cmHandleId = 'myHandle1' + def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']" + + def static propertyDataNodes = [new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp1']").withLeaves(['name': 'additionalProp1', 'value': 'additionalValue1']).build(), + new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp2']").withLeaves(['name': 'additionalProp2', 'value': 'additionalValue2']).build(), + new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/public-properties[@name='publicProp3']").withLeaves(['name': 'publicProp3', 'value': 'publicValue3']).build(), + new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/public-properties[@name='publicProp4']").withLeaves(['name': 'publicProp4', 'value': 'publicValue4']).build()] + def static cmHandleDataNodeAsCollection = [new DataNode(xpath: cmHandleXpath, childDataNodes: propertyDataNodes, leaves: ['id': cmHandleId])] + + def 'Update CM Handle Public Properties: #scenario'() { + given: 'the CPS service return a CM handle' + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> cmHandleDataNodeAsCollection + and: 'an update cm handle request with public properties updates' + def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: updatedPublicProperties)] + when: 'update data node leaves is called with the update request' + objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) + then: 'the replace list method is called with correct params' + 1 * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) >> { args -> + { + assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size() + assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate)) + } + } + where: 'following public properties updates are made' + scenario | updatedPublicProperties || expectedPropertiesAfterUpdate + 'property added' | ['newPubProp1': 'pub-val'] || [['publicProp3': 'publicValue3'], ['publicProp4': 'publicValue4'], ['newPubProp1': 'pub-val']] + 'property updated' | ['publicProp4': 'newPubVal'] || [['publicProp3': 'publicValue3'], ['publicProp4': 'newPubVal']] + 'property removed' | ['publicProp4': null] || [['publicProp3': 'publicValue3']] + 'property ignored(value is null)' | ['pub-prop': null] || [['publicProp3': 'publicValue3'], ['publicProp4': 'publicValue4']] + } + + def 'Update DMI Properties: #scenario'() { + given: 'the CPS service return a CM handle' + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> cmHandleDataNodeAsCollection + and: 'an update cm handle request with DMI properties updates' + def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: updatedDmiProperties)] + when: 'update data node leaves is called with the update request' + objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) + then: 'replace list method should is called with correct params' + expectedCallsToReplaceMethod * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) >> { args -> + { + assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size() + assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate)) + } + } + where: 'following DMI properties updates are made' + scenario | updatedDmiProperties || expectedPropertiesAfterUpdate | expectedCallsToReplaceMethod + 'property added' | ['newAdditionalProp1': 'add-value'] || [['additionalProp1': 'additionalValue1'], ['additionalProp2': 'additionalValue2'], ['newAdditionalProp1': 'add-value']] | 1 + 'property updated' | ['additionalProp1': 'newValue'] || [['additionalProp2': 'additionalValue2'], ['additionalProp1': 'newValue']] | 1 + 'property removed' | ['additionalProp1': null] || [['additionalProp2': 'additionalValue2']] | 1 + 'property ignored(value is null)' | ['new-prop': null] || [['additionalProp1': 'additionalValue1'], ['additionalProp2': 'additionalValue2']] | 1 + 'no property changes' | [:] || [['additionalProp1': 'additionalValue1'], ['additionalProp2': 'additionalValue2']] | 0 + } + + def 'Update CM Handle Properties, remove all properties: #scenario'() { + given: 'the CPS service return a CM handle' + def cmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId], childDataNodes: originalPropertyDataNodes) + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [cmHandleDataNode] + and: 'an update cm handle request that removes all public properties(existing and non-existing)' + def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp3': null, 'publicProp4': null])] + when: 'update data node leaves is called with the update request' + objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) + then: 'the replace list method is not called' + 0 * mockInventoryPersistence.replaceListContent(*_) + then: 'delete data node will be called for any existing property' + expectedCallsToDeleteDataNode * mockInventoryPersistence.deleteDataNode(_) >> { arg -> + { + assert arg[0].contains("@name='publicProp") + } + } + where: 'following public properties updates are made' + scenario | originalPropertyDataNodes || expectedCallsToDeleteDataNode + '2 original properties, both removed' | propertyDataNodes || 2 + 'no original properties' | [] || 0 + } + + def '#scenario error leads to #exception when we try to update cmHandle'() { + given: 'cm handles request' + def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: [:], dmiProperties: [:])] + and: 'data node cannot be found' + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(*_) >> { throw exception } + when: 'update data node leaves is called using correct parameters' + def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) + then: 'one failed registration response' + response.size() == 1 + and: 'it has expected error details' + with(response.get(0)) { + assert it.status == Status.FAILURE + assert it.cmHandle == cmHandleId + assert it.ncmpResponseStatus == expectedError + assert it.errorText == expectedErrorText + } + where: + scenario | cmHandleId | exception || expectedError | expectedErrorText + 'Cm Handle does not exist' | 'cmHandleId' | new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found' + 'Unknown' | 'cmHandleId' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' + 'Invalid cm handle id' | 'cmHandleId with spaces' | new DataValidationException('Name Validation Error.', cmHandleId + 'contains an invalid character') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id' + } + + def 'Multiple update operations in a single request'() { + given: 'cm handles request' + def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]), + new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]), + new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])] + and: 'data node can be found for 1st and 3rd cm-handle but not for 2nd cm-handle' + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(*_) >> cmHandleDataNodeAsCollection >> { + throw new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) } >> cmHandleDataNodeAsCollection + when: 'update data node leaves is called using correct parameters' + def cmHandleResponseList = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) + then: 'response has 3 values' + cmHandleResponseList.size() == 3 + and: 'the 1st and 3rd requests were processed successfully' + with(cmHandleResponseList.get(0)) { + assert it.status == Status.SUCCESS + assert it.cmHandle == cmHandleId + } + with(cmHandleResponseList.get(2)) { + assert it.status == Status.SUCCESS + assert it.cmHandle == cmHandleId + } + and: 'the 2nd request failed with correct error code' + with(cmHandleResponseList.get(1)) { + assert it.status == Status.FAILURE + assert it.cmHandle == cmHandleId + assert it.ncmpResponseStatus == CM_HANDLES_NOT_FOUND + assert it.errorText == 'cm handle id(s) not found' + } + then: 'the replace list method is called twice' + 2 * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) + } + + def 'Update alternate id of existing CM Handle.'() { + given: 'cm handles request' + def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')] + and: 'a data node found' + def dataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId, 'alternate-id': 'alt-1']) + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [dataNode] + when: 'cm handle properties is updated' + def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) + then: 'the update is delegated to cps data service with correct parameters' + 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _, ContentType.JSON) >> + { args -> + assert args[3].contains('alt-1') + } + and: 'one successful registration response' + response.size() == 1 + and: 'the response shows success for the given cm handle id' + assert response[0].status == Status.SUCCESS + assert response[0].cmHandle == cmHandleId + } + + def 'Update with rejected alternate id.'() { + given: 'cm handles request' + def updatedNcmpServiceCmHandles = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')] + and: 'a data node found' + def dataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId, 'alternate-id': 'alt-1']) + mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [dataNode] + when: 'attempt to update the cm handle' + def response = objectUnderTest.updateCmHandleProperties(updatedNcmpServiceCmHandles) + then: 'the update is NOT delegated to cps data service' + 0 * mockCpsDataService.updateNodeLeaves(*_) + and: 'the alternate id checker rejects the given cm handle (override default setup behavior)' + mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> [cmHandleId] + and: 'the response shows a failure for the given cm handle id' + assert response[0].status == Status.FAILURE + assert response[0].cmHandle == cmHandleId + } + + def 'Update CM Handle data producer identifier from #scenario'() { + given: 'an existing cm handle with no data producer identifier' + DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId','data-producer-identifier': oldDataProducerIdentifier]) + and: 'an update request with a new data producer identifier' + def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dataProducerIdentifier: 'New Data Producer Identifier') + when: 'data producer identifier updated' + objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle) + then: 'the update node leaves method is invoked once' + 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _, ContentType.JSON) >> { args -> + assert args[3].contains('New Data Producer Identifier') + } + and: 'correct information is logged' + def lastLoggingEvent = logger.list[0] + assert lastLoggingEvent.level == Level.DEBUG + assert lastLoggingEvent.formattedMessage.contains('Updating data-producer-identifier') + where: 'the following scenarios are attempted' + scenario | oldDataProducerIdentifier + 'null to something' | null + 'blank to something' | '' + } + + def 'Update CM Handle data producer identifier with same value'() { + given: 'an existing cm handle with no data producer identifier' + DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId','data-producer-identifier': 'same id']) + and: 'an update request with a new data producer identifier' + def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dataProducerIdentifier: 'same id') + when: 'data producer identifier updated' + objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle) + then: 'the update node leaves method is not invoked' + 0 * mockCpsDataService.updateNodeLeaves(*_) + } + + def 'Update CM Handle data producer identifier from some data producer identifier to another data producer identifier'() { + given: 'an existing cm handle with a data producer identifier' + DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId', 'data-producer-identifier': 'someDataProducerIdentifier']) + and: 'an update request with a new data producer identifier' + def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dataProducerIdentifier: 'someNewDataProducerIdentifier') + when: 'update data producer identifier is called with the update request' + objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle) + then: 'the update node leaves method is not invoked' + 0 * mockCpsDataService.updateNodeLeaves(*_) + and: 'correct information is logged' + def lastLoggingEvent = logger.list[0] + assert lastLoggingEvent.level == Level.WARN + assert lastLoggingEvent.formattedMessage.contains('Unable to update dataProducerIdentifier') + } + + def convertToProperties(expectedPropertiesAfterUpdateAsMap) { + def properties = [].withDefault { [:] } + expectedPropertiesAfterUpdateAsMap.forEach(property -> + property.forEach((key, val) -> { + properties.add(['name': key, 'value': val]) + })) + return properties + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServiceSpec.groovy new file mode 100644 index 0000000000..1c6e38420b --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/CmHandleRegistrationServiceSpec.groovy @@ -0,0 +1,458 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2022 Bell Canada + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl + +import com.hazelcast.map.IMap +import org.onap.cps.api.CpsDataService +import org.onap.cps.api.CpsModuleService +import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler +import org.onap.cps.ncmp.api.impl.exception.DmiRequestException +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService +import org.onap.cps.ncmp.api.impl.inventory.CmHandleState +import org.onap.cps.ncmp.api.impl.inventory.CompositeState +import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager +import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle +import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse +import org.onap.cps.ncmp.api.models.DmiPluginRegistration +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle +import org.onap.cps.ncmp.api.models.UpgradedCmHandles +import org.onap.cps.spi.exceptions.AlreadyDefinedException +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.exceptions.SchemaSetNotFoundException +import spock.lang.Specification + +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME +import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status + +class CmHandleRegistrationServiceSpec extends Specification { + + def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id') + def mockCpsModuleService = Mock(CpsModuleService) + def mockNetworkCmProxyDataServicePropertyHandler = Mock(CmHandleRegistrationServicePropertyHandler) + def mockInventoryPersistence = Mock(InventoryPersistence) + def mockCmHandleQueries = Mock(CmHandleQueryService) + def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler) + def mockCpsDataService = Mock(CpsDataService) + def mockModuleSyncStartedOnCmHandles = Mock(IMap) + def trustLevelPerDmiPlugin = [:] + def mockTrustLevelManager = Mock(TrustLevelManager) + def mockAlternateIdChecker = Mock(AlternateIdChecker) + + def objectUnderTest = Spy(new CmHandleRegistrationService( + mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCpsDataService, mockLcmEventsCmHandleStateHandler, + mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin , mockTrustLevelManager, mockAlternateIdChecker)) + + def setup() { + // always accept all cm handles + mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> [] + + // always can find all cm handles in DB + mockInventoryPersistence.getYangModelCmHandles(_) >> { args -> args[0].collect { new YangModelCmHandle(id:it) } } + } + + def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() { + given: 'a registration with operations of all types' + def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') + dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])]) + dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])]) + dmiRegistration.setRemovedCmHandles(['cmhandle-2']) + dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) + and: 'cm handles are persisted' + mockInventoryPersistence.getYangModelCmHandles(['cmhandle-2']) >> [new YangModelCmHandle()] + mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: CmHandleState.READY)) + and: 'cm handle is in READY state' + mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> true + when: 'registration is processed' + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) + then: 'cm-handles are removed first' + 1 * objectUnderTest.processRemovedCmHandles(*_) + and: 'de-registered cm handle entry is removed from in progress map' + 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2') + then: 'cm-handles are created' + 1 * objectUnderTest.processCreatedCmHandles(*_) + then: 'cm-handles are updated' + 1 * objectUnderTest.processUpdatedCmHandles(*_) + 1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> [] + then: 'cm-handles are upgraded' + 1 * objectUnderTest.processUpgradedCmHandles(*_) + } + + def 'DMI Registration upgrade operation with upgrade node state #scenario'() { + given: 'a registration with upgrade operation' + def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') + dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) + and: 'exception while checking cm handle state' + mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: cmHandleState)) + when: 'registration is processed' + def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) + then: 'upgrade operation contains expected error code' + assert result.upgradedCmHandles[0].status == expectedResponseStatus + where: 'the following parameters are used' + scenario | cmHandleState || expectedResponseStatus + 'READY' | CmHandleState.READY || Status.SUCCESS + 'Not READY' | CmHandleState.LOCKED || Status.FAILURE + } + + def 'DMI Registration upgrade with exception #scenario'() { + given: 'a registration with upgrade operation' + def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') + dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) + and: 'exception while checking cm handle state' + mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> { throw exception } + when: 'registration is processed' + def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) + then: 'upgrade operation contains expected error code' + assert result.upgradedCmHandles.ncmpResponseStatus.code[0] == expectedErrorCode + where: 'the following parameters are used' + scenario | exception || expectedErrorCode + 'data node not found' | new DataNodeNotFoundException('some-dataspace-name', 'some-anchor-name') || '100' + 'cm handle is invalid' | new DataValidationException('some error message', 'some error details') || '110' + } + + def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() { + given: 'a registration ' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin, + dmiDataPlugin: dmiDataPlugin) + dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] + when: 'update registration and sync module is called with correct DMI plugin information' + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'create cm handles registration and sync modules is called with the correct plugin information' + 1 * objectUnderTest.processCreatedCmHandles(dmiPluginRegistration, _) + and: 'dmi is added to the dmi trustLevel map' + assert trustLevelPerDmiPlugin.size() == 1 + assert trustLevelPerDmiPlugin.containsKey(expectedDmiPluginRegisteredName) + where: + scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedDmiPluginRegisteredName + 'combined DMI plugin' | 'service1' | '' | '' || 'service1' + 'data & model DMI plugins' | '' | 'service1' | 'service2' || 'service2' + 'data & model using same service' | '' | 'service1' | 'service1' || 'service1' + } + + def 'Create CM-handle Validation: Invalid DMI plugin service name with #scenario'() { + given: 'a registration ' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin, + dmiDataPlugin: dmiDataPlugin) + dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] + when: 'registration is called with incorrect DMI plugin information' + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'a DMI Request Exception is thrown with correct message details' + def exceptionThrown = thrown(DmiRequestException.class) + assert exceptionThrown.getMessage().contains(expectedMessageDetails) + and: 'registration is not called' + 0 * objectUnderTest.processCreatedCmHandles(*_) + where: + scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails + 'empty DMI plugins' | '' | '' | '' || 'No DMI plugin service names' + 'blank DMI plugins' | ' ' | ' ' | ' ' || 'No DMI plugin service names' + 'null DMI plugins' | null | null | null || 'No DMI plugin service names' + 'all DMI plugins' | 'service1' | 'service2' | 'service3' || 'Cannot register combined plugin service name and other service names' + '(combined)DMI and Data Plugin' | 'service1' | '' | 'service2' || 'Cannot register combined plugin service name and other service names' + '(combined)DMI and model Plugin' | 'service1' | 'service2' | '' || 'Cannot register combined plugin service name and other service names' + 'only model DMI plugin' | '' | 'service1' | '' || 'Cannot register just a Data or Model plugin service name' + 'only data DMI plugin' | '' | '' | 'service1' || 'Cannot register just a Data or Model plugin service name' + } + + def 'Create CM-Handle Successfully: #scenario.'() { + given: 'a registration without cm-handle properties' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') + dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle', dmiProperties: dmiProperties, publicProperties: publicProperties)] + when: 'registration is updated' + def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'a successful response is received' + response.createdCmHandles.size() == 1 + with(response.createdCmHandles[0]) { + assert it.status == Status.SUCCESS + assert it.cmHandle == 'cmhandle' + } + and: 'state handler is invoked with the expected parameters' + 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> { + args -> { + def yangModelCmHandles = args[0] + assert yangModelCmHandles.id == ['cmhandle'] + assert yangModelCmHandles.dmiServiceName == ['my-server'] + } + } + where: + scenario | dmiProperties | publicProperties || expectedDmiProperties | expectedPublicProperties + 'with dmi & public properties' | ['dmi-key': 'dmi-value'] | ['public-key': 'public-value'] || '[{"name":"dmi-key","value":"dmi-value"}]' | '[{"name":"public-key","value":"public-value"}]' + 'with only public properties' | [:] | ['public-key': 'public-value'] || [:] | '[{"name":"public-key","value":"public-value"}]' + 'with only dmi properties' | ['dmi-key': 'dmi-value'] | [:] || '[{"name":"dmi-key","value":"dmi-value"}]' | [:] + 'without dmi & public properties' | [:] | [:] || [:] | [:] + } + + def 'Add CM-Handle #scenario.'() { + given: ' registration details for one cm handles' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', + createdCmHandles:[new NcmpServiceCmHandle(cmHandleId: 'ch-1', registrationTrustLevel: registrationTrustLevel)]) + when: 'registration is updated' + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'trustLevel is set for the created cm-handle' + 1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(expectedMapping) + where: + scenario | registrationTrustLevel || expectedMapping + 'with trusted cm handle' | TrustLevel.COMPLETE || [ 'ch-1' : TrustLevel.COMPLETE ] + 'without trust level' | null || [ 'ch-1' : null ] + } + + def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed with some failures'() { + given: 'a registration with three cm-handles to be created' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', + createdCmHandles: [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1'), + new NcmpServiceCmHandle(cmHandleId: 'cmhandle2'), + new NcmpServiceCmHandle(cmHandleId: 'cmhandle3')]) + and: 'cm-handle creation is successful for 1st and 3rd; failed for 2nd' + def xpath = "somePathWithId[@id='cmhandle2']" + mockLcmEventsCmHandleStateHandler.initiateStateAdvised(*_) >> { throw AlreadyDefinedException.forDataNodes([xpath], 'some-context') } + when: 'registration is updated to create cm-handles' + def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'a response is received for all cm-handles' + response.createdCmHandles.size() == 1 + and: 'all cm-handles creation fails' + response.createdCmHandles.each { + assert it.cmHandle == 'cmhandle2' + assert it.status == Status.FAILURE + assert it.ncmpResponseStatus == CM_HANDLE_ALREADY_EXIST + assert it.errorText == 'cm-handle already exists' + } + } + + def 'Create CM-Handle Error Handling: Registration fails: #scenario'() { + given: 'a registration without cm-handle properties' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') + dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle')] + and: 'cm-handler registration fails: #scenario' + mockLcmEventsCmHandleStateHandler.initiateStateAdvised(*_) >> { throw exception } + when: 'registration is updated' + def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'a failure response is received' + response.createdCmHandles.size() == 1 + with(response.createdCmHandles[0]) { + assert it.status == Status.FAILURE + assert it.cmHandle == 'cmhandle' + assert it.ncmpResponseStatus == expectedError + assert it.errorText == expectedErrorText + } + where: + scenario | exception || expectedError | expectedErrorText + 'cm-handle already exist' | AlreadyDefinedException.forDataNodes(["path[@id='cmhandle']"], 'some-context') || CM_HANDLE_ALREADY_EXIST | 'cm-handle already exists' + 'unknown exception while registering cm-handle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' + } + + def 'Update CM-Handle: Update Operation Response is added to the response'() { + given: 'a registration to update CmHandles' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', updatedCmHandles: [{}]) + and: 'cm-handle updates can be processed successfully' + def updateOperationResponse = [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-1'), + CmHandleRegistrationResponse.createFailureResponse('cm-handle-2', new Exception("Failed")), + CmHandleRegistrationResponse.createFailureResponse('cm-handle-3', CM_HANDLES_NOT_FOUND), + CmHandleRegistrationResponse.createFailureResponse('cm handle 4', CM_HANDLE_INVALID_ID)] + mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(_) >> updateOperationResponse + when: 'registration is updated' + def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'the response contains updateOperationResponse' + assert response.updatedCmHandles.size() == 4 + assert response.updatedCmHandles.containsAll(updateOperationResponse) + } + + def 'Remove CmHandle Successfully: #scenario'() { + given: 'a registration' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', removedCmHandles: ['cmhandle']) + and: '#scenario' + mockCpsModuleService.deleteSchemaSetsWithCascade(_, ['cmhandle']) >> { if (!schemaSetExist) { throw new SchemaSetNotFoundException('', '') } } + when: 'registration is updated to delete cmhandle' + def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'the cmHandle state is updated to "DELETING"' + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> + { args -> args[0].values()[0] == CmHandleState.DELETING } + then: 'method to delete relevant schema set is called once' + 1 * mockInventoryPersistence.deleteSchemaSetsWithCascade(_) + and: 'method to delete relevant list/list element is called once' + 1 * mockInventoryPersistence.deleteDataNodes(_) + and: 'successful response is received' + assert response.removedCmHandles.size() == 1 + with(response.removedCmHandles[0]) { + assert it.status == Status.SUCCESS + assert it.cmHandle == 'cmhandle' + } + and: 'the cmHandle state is updated to "DELETED"' + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> + { args -> args[0].values()[0] == CmHandleState.DELETED } + and: 'No cm handles state updates for "upgraded cm handles"' + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:]) + where: + scenario | schemaSetExist + 'schema-set exists and can be deleted successfully' | true + 'schema-set does not exist' | false + } + + def 'Remove CmHandle: Partial Success'() { + given: 'a registration with three cm-handles to be deleted' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', + removedCmHandles: ['cmhandle1', 'cmhandle2', 'cmhandle3']) + and: 'cm-handle deletion fails on batch' + mockInventoryPersistence.deleteDataNodes(_) >> { throw new RuntimeException("Failed") } + and: 'cm-handle deletion is successful for 1st and 3rd; failed for 2nd' + mockInventoryPersistence.deleteDataNode("/dmi-registry/cm-handles[@id='cmhandle2']") >> { throw new RuntimeException("Failed") } + when: 'registration is updated to delete cmhandles' + def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'the cmHandle states are all updated to "DELETING"' + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch({ assert it.every { entry -> entry.value == CmHandleState.DELETING } }) + and: 'a response is received for all cm-handles' + response.removedCmHandles.size() == 3 + and: 'successfully de-registered cm handle 1 is removed from in progress map' + 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle1') + and: 'successfully de-registered cm handle 3 is removed from in progress map even though it was already being removed' + 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle3') >> 'already in progress' + and: 'failed de-registered cm handle entries should NOT be removed from in progress map' + 0 * mockModuleSyncStartedOnCmHandles.remove('cmhandle2') + and: '1st and 3rd cm-handle deletes successfully' + with(response.removedCmHandles[0]) { + assert it.status == Status.SUCCESS + assert it.cmHandle == 'cmhandle1' + } + with(response.removedCmHandles[2]) { + assert it.status == Status.SUCCESS + assert it.cmHandle == 'cmhandle3' + } + and: '2nd cm-handle deletion fails' + with(response.removedCmHandles[1]) { + assert it.status == Status.FAILURE + assert it.ncmpResponseStatus == UNKNOWN_ERROR + assert it.errorText == 'Failed' + assert it.cmHandle == 'cmhandle2' + } + and: 'the cmHandle state is updated to DELETED for 1st and 3rd' + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch({ + assert it.size() == 2 + assert it.every { entry -> entry.value == CmHandleState.DELETED } + }) + and: 'No cm handles state updates for "upgraded cm handles"' + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:]) + + } + + def 'Remove CmHandle Error Handling: Schema Set Deletion failed'() { + given: 'a registration' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', + removedCmHandles: ['cmhandle']) + and: 'schema set batch deletion failed with unknown error' + mockInventoryPersistence.deleteSchemaSetsWithCascade(_) >> { throw new RuntimeException('Failed') } + and: 'schema set single deletion failed with unknown error' + mockInventoryPersistence.deleteSchemaSetWithCascade(_) >> { throw new RuntimeException('Failed') } + when: 'registration is updated to delete cmhandle' + def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'no exception is thrown' + noExceptionThrown() + and: 'cm-handle is not deleted' + 0 * mockInventoryPersistence.deleteDataNodes(_) + and: 'the cmHandle state is not updated to "DELETED"' + 0 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([yangModelCmHandle: CmHandleState.DELETED]) + and: 'a failure response is received' + assert response.removedCmHandles.size() == 1 + with(response.removedCmHandles[0]) { + assert it.status == Status.FAILURE + assert it.cmHandle == 'cmhandle' + assert it.errorText == 'Failed' + assert it.ncmpResponseStatus == UNKNOWN_ERROR + } + } + + def 'Remove CmHandle Error Handling: #scenario'() { + given: 'a registration' + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', + removedCmHandles: ['cmhandle']) + and: 'cm-handle deletion fails on batch' + mockInventoryPersistence.deleteDataNodes(_) >> { throw deleteListElementException } + and: 'cm-handle deletion fails on individual delete' + mockInventoryPersistence.deleteDataNode(_) >> { throw deleteListElementException } + when: 'registration is updated to delete cmhandle' + def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'a failure response is received' + assert response.removedCmHandles.size() == 1 + with(response.removedCmHandles[0]) { + assert it.status == Status.FAILURE + assert it.cmHandle == 'cmhandle' + assert it.ncmpResponseStatus == expectedError + assert it.errorText == expectedErrorText + } + and: 'the cm handle state is not updated to "DELETED"' + 0 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_, CmHandleState.DELETED) + where: + scenario | cmHandleId | deleteListElementException || expectedError | expectedErrorText + 'cm-handle does not exist' | 'cmhandle' | new DataNodeNotFoundException('', '', '') || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found' + 'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException('', '') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id' + 'an unexpected exception' | 'cmhandle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' + } + + def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is #scenario'() { + given: 'an existing cm handle composite state' + def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag, + dataStores: CompositeState.DataStores.builder() + .operationalDataStore(CompositeState.Operational.builder() + .dataStoreSyncState(initialDataSyncState) + .build()).build()) + and: 'get cm handle state returns the composite state for the given cm handle id' + mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState + when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag' + objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag) + then: 'the data sync enabled flag is set to #dataSyncEnabled' + compositeState.dataSyncEnabled == dataSyncEnabledFlag + and: 'the data store sync state is set to #expectedDataStoreSyncState' + compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState + and: 'the cps data service to delete data nodes is invoked the expected number of times' + deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _) + and: 'the inventory persistence service to update node leaves is called with the correct values' + saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState) + where: 'the following data sync enabled flag is used' + scenario | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState || expectedDataStoreSyncState | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations + 'enabled' | true | false | DataStoreSyncState.NONE_REQUESTED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 1 + 'disabled' | false | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 0 | 1 + 'disabled where sync-state is currently SYNCHRONIZED' | false | true | DataStoreSyncState.SYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 1 | 1 + 'is set to existing flag state' | true | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 0 + } + + def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () { + given: 'a cm handle composite state' + def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false) + and: 'get cm handle state returns the composite state for the given cm handle id' + mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState + when: 'set data sync enabled is called with the data sync enabled flag set to true' + objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true) + then: 'the expected exception is thrown' + thrown(CpsException) + and: 'the inventory persistence service to update node leaves is not invoked' + 0 * mockInventoryPersistence.saveCmHandleState(_, _) + } + + + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NcmpCachedResourceRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NcmpCachedResourceRequestHandlerSpec.groovy new file mode 100644 index 0000000000..781b6204a9 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NcmpCachedResourceRequestHandlerSpec.groovy @@ -0,0 +1,64 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl + +import org.onap.cps.api.CpsDataService +import org.onap.cps.ncmp.api.NetworkCmProxyQueryService +import org.onap.cps.ncmp.api.models.CmResourceAddress +import org.onap.cps.spi.model.DataNode +import reactor.core.publisher.Mono +import spock.lang.Specification + +import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS + +class NcmpCachedResourceRequestHandlerSpec extends Specification { + + def cpsDataService = Mock(CpsDataService) + def networkCmProxyQueryService= Mock(NetworkCmProxyQueryService) + + def objectUnderTest = new NcmpCachedResourceRequestHandler(cpsDataService, networkCmProxyQueryService) + + def 'Execute a request with include descendants = #includeDescendants.'() { + when: 'executing a request' + objectUnderTest.executeRequest('ch-1', 'resource', includeDescendants) + then: 'it is delegated to the ncmp query service with the correct option' + 1 * networkCmProxyQueryService.queryResourceDataOperational('ch-1','resource', expectedFetchDescendantsOption) + where: 'the following options are used' + includeDescendants || expectedFetchDescendantsOption + true || INCLUDE_ALL_DESCENDANTS + false || OMIT_DESCENDANTS + } + + def 'Get resource data.'() { + given: 'the data service returns 2 nodes for the given resource address' + def cmResourceAddress = new CmResourceAddress('datastore','ch-1','resource') + def dataNode1 = new DataNode(xpath:'p1') + def dataNode2 = new DataNode(xpath:'p2') + cpsDataService.getDataNodes('datastore','ch-1','resource',OMIT_DESCENDANTS) >> [dataNode1, dataNode2] + when: 'getting the resource data' + def result = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, 'options', 'topic', 'request id', false, 'authorization') + then: 'the result is a "Mono" holding just the first data node' + assert result instanceof Mono + assert result.block() == dataNode1 + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NcmpDatastoreRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NcmpDatastoreRequestHandlerSpec.groovy index b73f9a46d4..9a845c0ba9 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NcmpDatastoreRequestHandlerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NcmpDatastoreRequestHandlerSpec.groovy @@ -20,48 +20,44 @@ package org.onap.cps.ncmp.api.impl -import org.onap.cps.ncmp.api.NetworkCmProxyDataService import org.onap.cps.ncmp.api.impl.exception.InvalidDatastoreException import org.onap.cps.ncmp.api.impl.exception.InvalidOperationException +import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations import org.onap.cps.ncmp.api.models.CmResourceAddress import org.onap.cps.ncmp.api.models.DataOperationDefinition import org.onap.cps.ncmp.api.models.DataOperationRequest import org.onap.cps.ncmp.exceptions.OperationNotSupportedException import org.onap.cps.ncmp.exceptions.PayloadTooLargeException -import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import reactor.core.publisher.Mono import spock.lang.Specification +import static org.springframework.http.HttpStatus.I_AM_A_TEAPOT + class NcmpDatastoreRequestHandlerSpec extends Specification { - def mockNetworkCmProxyDataService = Mock(NetworkCmProxyDataService) + def dmiDataOperations = Mock(DmiDataOperations) - def objectUnderTest = new NcmpPassthroughResourceRequestHandler(mockNetworkCmProxyDataService) + def objectUnderTest = new NcmpPassthroughResourceRequestHandler(dmiDataOperations) + def NO_TOPIC = null def NO_AUTH_HEADER = null - def setup() { - objectUnderTest.timeOutInMilliSeconds = 100 - } - def 'Attempt to execute async get request with #scenario.'() { given: 'notification feature is turned on/off' objectUnderTest.notificationFeatureEnabled = notificationFeatureEnabled and: 'a CM resource address' def cmResourceAddress = new CmResourceAddress('ds', 'ch1', 'resource1') - and: 'the (mocked) service when called with the correct parameters returns a response from dmi' - def resultFromDmi = new ResponseEntity('response from dmi',HttpStatus.I_AM_A_TEAPOT) - def synchronousResult = Mono.justOrEmpty(resultFromDmi) - mockNetworkCmProxyDataService.getResourceDataForCmHandle(cmResourceAddress, 'options', _, _, NO_AUTH_HEADER) >> synchronousResult + and: 'the (mocked) service when called with the correct parameters (with or without topic) returns a response from dmi' + def dmiResponse = Mono.justOrEmpty(new ResponseEntity('dmi response',I_AM_A_TEAPOT)) + dmiDataOperations.getResourceDataFromDmi(cmResourceAddress, 'options', NO_TOPIC, _, NO_AUTH_HEADER) >> dmiResponse + dmiDataOperations.getResourceDataFromDmi(cmResourceAddress, 'options', topic, _, NO_AUTH_HEADER) >> dmiResponse when: 'get request is executed with topic = #topic' def response = objectUnderTest.executeRequest(cmResourceAddress, 'options', topic, false, NO_AUTH_HEADER) then: 'a successful result with/without request id is returned' if (expectSynchronousResponse) { - assert response.toString().contains('response from dmi') - assert response.toString().contains("I'm a teapot") - } else { - // expect request id in a map + assert response == 'dmi response' + } else { // expect request id in a map assert response.keySet()[0] == 'requestId' } where: 'the following parameters are used' @@ -74,33 +70,20 @@ class NcmpDatastoreRequestHandlerSpec extends Specification { def 'Attempt to execute async data operation request with feature #scenario.'() { given: 'a extended request handler that supports bulk requests' - def objectUnderTest = new NcmpPassthroughResourceRequestHandler(mockNetworkCmProxyDataService) + def objectUnderTest = new NcmpPassthroughResourceRequestHandler(dmiDataOperations) and: 'notification feature is turned on/off' objectUnderTest.notificationFeatureEnabled = notificationFeatureEnabled when: 'data operation request is executed' - objectUnderTest.executeRequest('someTopic', new DataOperationRequest(), NO_AUTH_HEADER) + def dataOperationDefinition = new DataOperationDefinition(operation: 'read', datastore: 'ncmp-datastore:passthrough-running', cmHandleIds: ['ch']) + def result = objectUnderTest.executeAsynchronousRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER) then: 'the task is executed in an async fashion or not' - expectedCalls * mockNetworkCmProxyDataService.executeDataOperationForCmHandles('someTopic', _, _, null) + expectedCalls * dmiDataOperations.requestResourceDataFromDmi('someTopic', _, _, NO_AUTH_HEADER) + and: + result.keySet()[0] == expectedKeyInMap where: 'the following parameters are used' - scenario | notificationFeatureEnabled || expectedCalls - 'on' | true || 1 - 'off' | false || 0 - } - - def 'Execute async data operation request with datastore #datastore.'() { - given: 'notification feature is turned on' - objectUnderTest.notificationFeatureEnabled = true - and: 'a data operation request with datastore: #datastore' - def dataOperationDefinition = new DataOperationDefinition(operation: 'read', datastore: datastore) - def dataOperationRequest = new DataOperationRequest(dataOperationDefinitions: [dataOperationDefinition]) - when: 'data operation request is executed' - def response = objectUnderTest.executeRequest('myTopic', dataOperationRequest, NO_AUTH_HEADER) - and: 'a map with request id is returned' - assert response.keySet()[0] == 'requestId' - then: 'the network service is invoked' - 1 * mockNetworkCmProxyDataService.executeDataOperationForCmHandles('myTopic', dataOperationRequest, _, NO_AUTH_HEADER) - where: 'the following datastores are used' - datastore << ['ncmp-datastore:passthrough-running', 'ncmp-datastore:passthrough-operational'] + scenario | notificationFeatureEnabled || expectedCalls || expectedKeyInMap + 'on' | true || 1 || 'requestId' + 'off' | false || 0 || 'status' } def 'Attempt to execute async data operation request with error #scenario'() { @@ -108,7 +91,7 @@ class NcmpDatastoreRequestHandlerSpec extends Specification { def dataOperationDefinition = new DataOperationDefinition(operation: 'read', datastore: datastore) when: 'data operation request is executed' def dataOperationRequest = new DataOperationRequest(dataOperationDefinitions: [dataOperationDefinition]) - objectUnderTest.executeRequest('myTopic', dataOperationRequest, NO_AUTH_HEADER) + objectUnderTest.executeAsynchronousRequest('myTopic', dataOperationRequest, NO_AUTH_HEADER) then: 'the correct error is thrown' def thrown = thrown(InvalidDatastoreException) assert thrown.message.contains(expectedErrorMessage) @@ -122,7 +105,7 @@ class NcmpDatastoreRequestHandlerSpec extends Specification { given: 'a data operation definition with operation: #operation' def dataOperationDefinition = new DataOperationDefinition(operation: operation, datastore: 'ncmp-datastore:passthrough-running') when: 'data operation request is executed' - objectUnderTest.executeRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER) + objectUnderTest.executeAsynchronousRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER) then: 'the expected type of exception is thrown' thrown(expectedException) where: 'the following operations are used' @@ -136,11 +119,11 @@ class NcmpDatastoreRequestHandlerSpec extends Specification { def 'Attempt to execute async data operation request with too many cm handles.'() { given: 'a data operation definition with too many cm handles' - def tooMany = objectUnderTest.MAXIMUM_CM_HANDLES_PER_OPERATION+1 + def tooMany = objectUnderTest.MAXIMUM_CM_HANDLES_PER_OPERATION + 1 def cmHandleIds = new String[tooMany] def dataOperationDefinition = new DataOperationDefinition(operationId: 'abc', operation: 'read', datastore: 'ncmp-datastore:passthrough-running', cmHandleIds: cmHandleIds) when: 'data operation request is executed' - objectUnderTest.executeRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER) + objectUnderTest.executeAsynchronousRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER) then: 'a payload too large exception is thrown' def exceptionThrown = thrown(PayloadTooLargeException) and: 'the error message contains the offending number of cm handles' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy deleted file mode 100644 index 7c410cc58a..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy +++ /dev/null @@ -1,223 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl - -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT - -import org.onap.cps.cpspath.parser.PathParsingException -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueriesImpl -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle -import org.onap.cps.spi.FetchDescendantsOption -import org.onap.cps.spi.exceptions.DataInUseException -import org.onap.cps.spi.exceptions.DataValidationException -import org.onap.cps.spi.model.ConditionProperties -import org.onap.cps.spi.model.DataNode -import spock.lang.Specification - -class NetworkCmProxyCmHandleQueryServiceSpec extends Specification { - - def cmHandleQueries = Mock(CmHandleQueries) - def partiallyMockedCmHandleQueries = Spy(CmHandleQueries) - def mockInventoryPersistence = Mock(InventoryPersistence) - - def dmiRegistry = new DataNode(xpath: NCMP_DMI_REGISTRY_PARENT, childDataNodes: createDataNodeList(['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4'])) - - def objectUnderTest = new NetworkCmProxyCmHandleQueryServiceImpl(cmHandleQueries, mockInventoryPersistence) - def objectUnderTestWithPartiallyMockedQueries = new NetworkCmProxyCmHandleQueryServiceImpl(partiallyMockedCmHandleQueries, mockInventoryPersistence) - - def 'Query cm handle ids with cpsPath.'() { - given: 'a cmHandleWithCpsPath condition property' - def cmHandleQueryParameters = new CmHandleQueryServiceParameters() - def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']]) - cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) - and: 'the query get the cm handle datanodes excluding all descendants returns a datanode' - cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])] - when: 'the query is executed for cm handle ids' - def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) - then: 'the correct expected cm handles ids are returned' - assert result == ['some-cmhandle-id'] as Set - } - - def 'Cm handle ids query with error: #scenario.'() { - given: 'a cmHandleWithCpsPath condition property' - def cmHandleQueryParameters = new CmHandleQueryServiceParameters() - def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']]) - cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) - and: 'cmHandleQueries throws a path parsing exception' - cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> { throw thrownException } - when: 'the query is executed for cm handle ids' - objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) - then: 'a data validation exception is thrown' - thrown(expectedException) - where: 'the following data is used' - scenario | thrownException || expectedException - 'PathParsingException' | new PathParsingException('some message', 'some details') || DataValidationException - 'any other Exception' | new DataInUseException('some message', 'some details') || DataInUseException - } - - def 'Cm handle ids cpsPath query for private properties (not allowed).'() { - given: 'a CpsPath condition property for private properties' - def cmHandleQueryParameters = new CmHandleQueryServiceParameters() - def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/additional-properties']]) - cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) - when: 'the query is executed for cm handle ids' - def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) - then: 'empty result is returned' - assert result.isEmpty() - } - - def 'Query cm handle ids with module names when #scenario from query.'() { - given: 'a modules condition property' - def cmHandleQueryParameters = new CmHandleQueryServiceParameters() - def conditionProperties = createConditionProperties('hasAllModules', [['moduleName': 'some-module-name']]) - cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) - when: 'the query is executed for cm handle ids' - def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) - then: 'the inventory service is called with the correct module names' - 1 * mockInventoryPersistence.getCmHandleIdsWithGivenModules(['some-module-name']) >> cmHandleIdsFromService - and: 'the correct expected cm handles ids are returned' - assert result.size() == cmHandleIdsFromService.size() - assert result.containsAll(cmHandleIdsFromService) - where: 'the following data is used' - scenario | cmHandleIdsFromService - 'One anchor returned' | ['some-cmhandle-id'] - 'No anchors are returned' | [] - } - - def 'Query cm handles with some trust level query parameters'() { - given: 'a trust level condition property' - def trustLevelQueryParameters = new CmHandleQueryServiceParameters() - def trustLevelConditionProperties = createConditionProperties('cmHandleWithTrustLevel', [['trustLevel': 'COMPLETE'] as Map]) - trustLevelQueryParameters.setCmHandleQueryParameters([trustLevelConditionProperties]) - when: 'the query is being executed' - objectUnderTest.queryCmHandleIds(trustLevelQueryParameters) - then: 'the query is being delegated to the cm handle query service with correct parameter' - 1 * cmHandleQueries.queryCmHandlesByTrustLevel(['trustLevel': 'COMPLETE'] as Map) - } - - def 'Query cm handle details with module names when #scenario from query.'() { - given: 'a modules condition property' - def cmHandleQueryParameters = new CmHandleQueryServiceParameters() - def conditionProperties = createConditionProperties('hasAllModules', [['moduleName': 'some-module-name']]) - cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) - when: 'the query is executed for cm handle ids' - def result = objectUnderTest.queryCmHandles(cmHandleQueryParameters) - then: 'the inventory service is called with the correct module names' - 1 * mockInventoryPersistence.getCmHandleIdsWithGivenModules(['some-module-name']) >> ['ch1'] - and: 'the inventory service is called with teh correct if and returns a yang model cm handle' - 1 * mockInventoryPersistence.getYangModelCmHandles(['ch1']) >> - [new YangModelCmHandle(id: 'abc', dmiProperties: [new YangModelCmHandle.Property('name','value')], publicProperties: [])] - and: 'the expected cm handle(s) are returned as NCMP Service cm handles' - assert result[0] instanceof NcmpServiceCmHandle - assert result.size() == 1 - assert result[0].dmiProperties == [name:'value'] - } - - def 'Query cm handle ids when the query is empty.'() { - given: 'We use an empty query' - def cmHandleQueryParameters = new CmHandleQueryServiceParameters() - and: 'the inventory persistence returns the dmi registry datanode with just ids' - mockInventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT, FetchDescendantsOption.DIRECT_CHILDREN_ONLY) >> [dmiRegistry] - when: 'the query is executed for both cm handle ids' - def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) - then: 'the correct expected cm handles are returned' - assert result.containsAll('PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4') - } - - def 'Query cm handle details when the query is empty.'() { - given: 'We use an empty query' - def cmHandleQueryParameters = new CmHandleQueryServiceParameters() - and: 'the inventory persistence returns the dmi registry datanode with just ids' - mockInventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT) >> [dmiRegistry] - when: 'the query is executed for both cm handle details' - def result = objectUnderTest.queryCmHandles(cmHandleQueryParameters) - then: 'the correct cm handles are returned' - assert result.size() == 4 - assert result.cmHandleId.containsAll('PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4') - } - - def 'Query CMHandleId with #scenario.' () { - given: 'a query object created with #condition' - def cmHandleQueryParameters = new CmHandleQueryServiceParameters() - def conditionProperties = createConditionProperties(conditionName, [['some-key': 'some-value']]) - cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) - and: 'the inventoryPersistence returns different CmHandleIds' - partiallyMockedCmHandleQueries.queryCmHandlePublicProperties(*_) >> cmHandlesWithMatchingPublicProperties - partiallyMockedCmHandleQueries.queryCmHandleAdditionalProperties(*_) >> cmHandlesWithMatchingPrivateProperties - when: 'the query executed' - def result = objectUnderTestWithPartiallyMockedQueries.queryCmHandleIdsForInventory(cmHandleQueryParameters) - then: 'the expected number of results are returned.' - assert result.size() == expectedCmHandleIdsSize - where: 'the following data is used' - scenario | conditionName | cmHandlesWithMatchingPublicProperties | cmHandlesWithMatchingPrivateProperties || expectedCmHandleIdsSize - 'all properties, only public matching' | 'hasAllProperties' | ['h1', 'h2'] | null || 2 - 'all properties, no matching cm handles' | 'hasAllProperties' | [] | [] || 0 - 'additional properties, some matching cm handles' | 'hasAllAdditionalProperties' | [] | ['h1', 'h2'] || 2 - 'additional properties, no matching cm handles' | 'hasAllAdditionalProperties' | null | [] || 0 - } - - def 'Retrieve CMHandleIds by different DMI properties with #scenario.' () { - given: 'a query object created with dmi plugin as condition' - def cmHandleQueryParameters = new CmHandleQueryServiceParameters() - def conditionProperties = createConditionProperties('cmHandleWithDmiPlugin', [['some-key': 'some-value']]) - cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) - and: 'the inventoryPersistence returns different CmHandleIds' - partiallyMockedCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier(*_) >> cmHandleQueryResult - when: 'the query executed' - def result = objectUnderTestWithPartiallyMockedQueries.queryCmHandleIdsForInventory(cmHandleQueryParameters) - then: 'the expected number of results are returned.' - assert result.size() == expectedCmHandleIdsSize - where: 'the following data is used' - scenario | cmHandleQueryResult || expectedCmHandleIdsSize - 'some matches' | ['h1','h2'] || 2 - 'no matches' | [] || 0 - } - - def 'Combine two query results where #scenario.'() { - when: 'two query results in the form of a map of NcmpServiceCmHandles are combined into a single query result' - def result = objectUnderTest.combineCmHandleQueryResults(firstQuery, secondQuery) - then: 'the returned result is the same as the expected result' - result == expectedResult - where: - scenario | firstQuery | secondQuery || expectedResult - 'two queries with unique and non unique entries exist' | ['PNFDemo', 'PNFDemo2'] | ['PNFDemo', 'PNFDemo3'] || ['PNFDemo'] - 'the first query contains entries and second query is empty' | ['PNFDemo', 'PNFDemo2'] | [] || [] - 'the second query contains entries and first query is empty' | [] | ['PNFDemo', 'PNFDemo3'] || [] - 'the first query contains entries and second query is null' | ['PNFDemo', 'PNFDemo2'] | null || ['PNFDemo', 'PNFDemo2'] - 'the second query contains entries and first query is null' | null | ['PNFDemo', 'PNFDemo3'] || ['PNFDemo', 'PNFDemo3'] - 'both queries are empty' | [] | [] || [] - 'both queries are null' | null | null || null - } - - def createConditionProperties(String conditionName, List> conditionParameters) { - return new ConditionProperties(conditionName : conditionName, conditionParameters : conditionParameters) - } - - def static createDataNodeList(dataNodeIds) { - def dataNodes =[] - dataNodeIds.each{ dataNodes << new DataNode(xpath: "/dmi-registry/cm-handles[@id='${it}']", leaves: ['id':it]) } - return dataNodes - } -} 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 deleted file mode 100644 index fbfbac59e0..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy +++ /dev/null @@ -1,421 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation - * Modifications Copyright (C) 2022 Bell Canada - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl - -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID -import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR - -import org.onap.cps.ncmp.api.impl.inventory.CompositeState -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager -import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker -import org.onap.cps.ncmp.api.models.UpgradedCmHandles -import com.fasterxml.jackson.databind.ObjectMapper -import com.hazelcast.map.IMap -import org.onap.cps.api.CpsDataService -import org.onap.cps.api.CpsModuleService -import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService -import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler -import org.onap.cps.ncmp.api.impl.exception.DmiRequestException -import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence -import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse -import org.onap.cps.ncmp.api.models.DmiPluginRegistration -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle -import org.onap.cps.spi.exceptions.AlreadyDefinedException -import org.onap.cps.spi.exceptions.DataNodeNotFoundException -import org.onap.cps.spi.exceptions.DataValidationException -import org.onap.cps.spi.exceptions.SchemaSetNotFoundException -import org.onap.cps.utils.JsonObjectMapper -import spock.lang.Specification - -class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { - - def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id') - def mockCpsModuleService = Mock(CpsModuleService) - def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) - def mockDmiDataOperations = Mock(DmiDataOperations) - def mockNetworkCmProxyDataServicePropertyHandler = Mock(NetworkCmProxyDataServicePropertyHandler) - def mockInventoryPersistence = Mock(InventoryPersistence) - def mockCmHandleQueries = Mock(CmHandleQueries) - def stubbedNetworkCmProxyCmHandlerQueryService = Stub(NetworkCmProxyCmHandleQueryService) - def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler) - def mockCpsDataService = Mock(CpsDataService) - def mockModuleSyncStartedOnCmHandles = Mock(IMap) - def trustLevelPerDmiPlugin = [:] - def mockTrustLevelManager = Mock(TrustLevelManager) - def mockAlternateIdChecker = Mock(AlternateIdChecker) - - def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations, - mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries, - stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService, - mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockAlternateIdChecker)) - - def setup() { - // always accept all cm handles - mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> [] - - // always can find all cm handles in DB - mockInventoryPersistence.getYangModelCmHandles(_) >> { args -> args[0].collect { new YangModelCmHandle(id:it) } } - } - - def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() { - given: 'a registration with operations of all types' - def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') - dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])]) - dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])]) - dmiRegistration.setRemovedCmHandles(['cmhandle-2']) - dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) - and: 'cm handles are persisted' - mockInventoryPersistence.getYangModelCmHandles(['cmhandle-2']) >> [new YangModelCmHandle()] - mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: CmHandleState.READY)) - and: 'cm handle is in READY state' - mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> true - when: 'registration is processed' - objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) - then: 'cm-handles are removed first' - 1 * objectUnderTest.processRemovedCmHandles(*_) - and: 'de-registered cm handle entry is removed from in progress map' - 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2') - then: 'cm-handles are created' - 1 * objectUnderTest.processCreatedCmHandles(*_) - then: 'cm-handles are updated' - 1 * objectUnderTest.processUpdatedCmHandles(*_) - 1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> [] - then: 'cm-handles are upgraded' - 1 * objectUnderTest.processUpgradedCmHandles(*_) - } - - def 'DMI Registration upgrade operation with upgrade node state #scenario'() { - given: 'a registration with upgrade operation' - def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') - dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) - and: 'exception while checking cm handle state' - mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: cmHandleState)) - when: 'registration is processed' - def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) - then: 'upgrade operation contains expected error code' - assert result.upgradedCmHandles[0].status == expectedResponseStatus - where: 'the following parameters are used' - scenario | cmHandleState || expectedResponseStatus - 'READY' | CmHandleState.READY || Status.SUCCESS - 'Not READY' | CmHandleState.LOCKED || Status.FAILURE - } - - def 'DMI Registration upgrade with exception #scenario'() { - given: 'a registration with upgrade operation' - def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') - dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) - and: 'exception while checking cm handle state' - mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> { throw exception } - when: 'registration is processed' - def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) - then: 'upgrade operation contains expected error code' - assert result.upgradedCmHandles.ncmpResponseStatus.code[0] == expectedErrorCode - where: 'the following parameters are used' - scenario | exception || expectedErrorCode - 'data node not found' | new DataNodeNotFoundException('some-dataspace-name', 'some-anchor-name') || '100' - 'cm handle is invalid' | new DataValidationException('some error message', 'some error details') || '110' - } - - def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() { - given: 'a registration ' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin, - dmiDataPlugin: dmiDataPlugin) - dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] - when: 'update registration and sync module is called with correct DMI plugin information' - objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'create cm handles registration and sync modules is called with the correct plugin information' - 1 * objectUnderTest.processCreatedCmHandles(dmiPluginRegistration, _) - and: 'dmi is added to the dmi trustLevel map' - assert trustLevelPerDmiPlugin.size() == 1 - assert trustLevelPerDmiPlugin.containsKey(expectedDmiPluginRegisteredName) - where: - scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedDmiPluginRegisteredName - 'combined DMI plugin' | 'service1' | '' | '' || 'service1' - 'data & model DMI plugins' | '' | 'service1' | 'service2' || 'service2' - 'data & model using same service' | '' | 'service1' | 'service1' || 'service1' - } - - def 'Create CM-handle Validation: Invalid DMI plugin service name with #scenario'() { - given: 'a registration ' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin, - dmiDataPlugin: dmiDataPlugin) - dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] - when: 'registration is called with incorrect DMI plugin information' - objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'a DMI Request Exception is thrown with correct message details' - def exceptionThrown = thrown(DmiRequestException.class) - assert exceptionThrown.getMessage().contains(expectedMessageDetails) - and: 'registration is not called' - 0 * objectUnderTest.processCreatedCmHandles(*_) - where: - scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails - 'empty DMI plugins' | '' | '' | '' || 'No DMI plugin service names' - 'blank DMI plugins' | ' ' | ' ' | ' ' || 'No DMI plugin service names' - 'null DMI plugins' | null | null | null || 'No DMI plugin service names' - 'all DMI plugins' | 'service1' | 'service2' | 'service3' || 'Cannot register combined plugin service name and other service names' - '(combined)DMI and Data Plugin' | 'service1' | '' | 'service2' || 'Cannot register combined plugin service name and other service names' - '(combined)DMI and model Plugin' | 'service1' | 'service2' | '' || 'Cannot register combined plugin service name and other service names' - 'only model DMI plugin' | '' | 'service1' | '' || 'Cannot register just a Data or Model plugin service name' - 'only data DMI plugin' | '' | '' | 'service1' || 'Cannot register just a Data or Model plugin service name' - } - - def 'Create CM-Handle Successfully: #scenario.'() { - given: 'a registration without cm-handle properties' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') - dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle', dmiProperties: dmiProperties, publicProperties: publicProperties)] - when: 'registration is updated' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'a successful response is received' - response.createdCmHandles.size() == 1 - with(response.createdCmHandles[0]) { - assert it.status == Status.SUCCESS - assert it.cmHandle == 'cmhandle' - } - and: 'state handler is invoked with the expected parameters' - 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> { - args -> { - def yangModelCmHandles = args[0] - assert yangModelCmHandles.id == ['cmhandle'] - assert yangModelCmHandles.dmiServiceName == ['my-server'] - } - } - where: - scenario | dmiProperties | publicProperties || expectedDmiProperties | expectedPublicProperties - 'with dmi & public properties' | ['dmi-key': 'dmi-value'] | ['public-key': 'public-value'] || '[{"name":"dmi-key","value":"dmi-value"}]' | '[{"name":"public-key","value":"public-value"}]' - 'with only public properties' | [:] | ['public-key': 'public-value'] || [:] | '[{"name":"public-key","value":"public-value"}]' - 'with only dmi properties' | ['dmi-key': 'dmi-value'] | [:] || '[{"name":"dmi-key","value":"dmi-value"}]' | [:] - 'without dmi & public properties' | [:] | [:] || [:] | [:] - } - - def 'Add CM-Handle #scenario.'() { - given: ' registration details for one cm handles' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', - createdCmHandles:[new NcmpServiceCmHandle(cmHandleId: 'ch-1', registrationTrustLevel: registrationTrustLevel)]) - when: 'registration is updated' - objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'trustLevel is set for the created cm-handle' - 1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(expectedMapping) - where: - scenario | registrationTrustLevel || expectedMapping - 'with trusted cm handle' | TrustLevel.COMPLETE || [ 'ch-1' : TrustLevel.COMPLETE ] - 'without trust level' | null || [ 'ch-1' : null ] - } - - def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed with some failures'() { - given: 'a registration with three cm-handles to be created' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', - createdCmHandles: [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1'), - new NcmpServiceCmHandle(cmHandleId: 'cmhandle2'), - new NcmpServiceCmHandle(cmHandleId: 'cmhandle3')]) - and: 'cm-handle creation is successful for 1st and 3rd; failed for 2nd' - def xpath = "somePathWithId[@id='cmhandle2']" - mockLcmEventsCmHandleStateHandler.initiateStateAdvised(*_) >> { throw AlreadyDefinedException.forDataNodes([xpath], 'some-context') } - when: 'registration is updated to create cm-handles' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'a response is received for all cm-handles' - response.createdCmHandles.size() == 1 - and: 'all cm-handles creation fails' - response.createdCmHandles.each { - assert it.cmHandle == 'cmhandle2' - assert it.status == Status.FAILURE - assert it.ncmpResponseStatus == CM_HANDLE_ALREADY_EXIST - assert it.errorText == 'cm-handle already exists' - } - } - - def 'Create CM-Handle Error Handling: Registration fails: #scenario'() { - given: 'a registration without cm-handle properties' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') - dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle')] - and: 'cm-handler registration fails: #scenario' - mockLcmEventsCmHandleStateHandler.initiateStateAdvised(*_) >> { throw exception } - when: 'registration is updated' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'a failure response is received' - response.createdCmHandles.size() == 1 - with(response.createdCmHandles[0]) { - assert it.status == Status.FAILURE - assert it.cmHandle == 'cmhandle' - assert it.ncmpResponseStatus == expectedError - assert it.errorText == expectedErrorText - } - where: - scenario | exception || expectedError | expectedErrorText - 'cm-handle already exist' | AlreadyDefinedException.forDataNodes(["path[@id='cmhandle']"], 'some-context') || CM_HANDLE_ALREADY_EXIST | 'cm-handle already exists' - 'unknown exception while registering cm-handle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' - } - - def 'Update CM-Handle: Update Operation Response is added to the response'() { - given: 'a registration to update CmHandles' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', updatedCmHandles: [{}]) - and: 'cm-handle updates can be processed successfully' - def updateOperationResponse = [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-1'), - CmHandleRegistrationResponse.createFailureResponse('cm-handle-2', new Exception("Failed")), - CmHandleRegistrationResponse.createFailureResponse('cm-handle-3', CM_HANDLES_NOT_FOUND), - CmHandleRegistrationResponse.createFailureResponse('cm handle 4', CM_HANDLE_INVALID_ID)] - mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(_) >> updateOperationResponse - when: 'registration is updated' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'the response contains updateOperationResponse' - assert response.updatedCmHandles.size() == 4 - assert response.updatedCmHandles.containsAll(updateOperationResponse) - } - - def 'Remove CmHandle Successfully: #scenario'() { - given: 'a registration' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', removedCmHandles: ['cmhandle']) - and: '#scenario' - mockCpsModuleService.deleteSchemaSetsWithCascade(_, ['cmhandle']) >> { if (!schemaSetExist) { throw new SchemaSetNotFoundException('', '') } } - when: 'registration is updated to delete cmhandle' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'the cmHandle state is updated to "DELETING"' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> - { args -> args[0].values()[0] == CmHandleState.DELETING } - then: 'method to delete relevant schema set is called once' - 1 * mockInventoryPersistence.deleteSchemaSetsWithCascade(_) - and: 'method to delete relevant list/list element is called once' - 1 * mockInventoryPersistence.deleteDataNodes(_) - and: 'successful response is received' - assert response.removedCmHandles.size() == 1 - with(response.removedCmHandles[0]) { - assert it.status == Status.SUCCESS - assert it.cmHandle == 'cmhandle' - } - and: 'the cmHandle state is updated to "DELETED"' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> - { args -> args[0].values()[0] == CmHandleState.DELETED } - and: 'No cm handles state updates for "upgraded cm handles"' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:]) - where: - scenario | schemaSetExist - 'schema-set exists and can be deleted successfully' | true - 'schema-set does not exist' | false - } - - def 'Remove CmHandle: Partial Success'() { - given: 'a registration with three cm-handles to be deleted' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', - removedCmHandles: ['cmhandle1', 'cmhandle2', 'cmhandle3']) - and: 'cm-handle deletion fails on batch' - mockInventoryPersistence.deleteDataNodes(_) >> { throw new RuntimeException("Failed") } - and: 'cm-handle deletion is successful for 1st and 3rd; failed for 2nd' - mockInventoryPersistence.deleteDataNode("/dmi-registry/cm-handles[@id='cmhandle2']") >> { throw new RuntimeException("Failed") } - when: 'registration is updated to delete cmhandles' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'the cmHandle states are all updated to "DELETING"' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch({ assert it.every { entry -> entry.value == CmHandleState.DELETING } }) - and: 'a response is received for all cm-handles' - response.removedCmHandles.size() == 3 - and: 'successfully de-registered cm handle 1 is removed from in progress map' - 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle1') - and: 'successfully de-registered cm handle 3 is removed from in progress map even though it was already being removed' - 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle3') >> 'already in progress' - and: 'failed de-registered cm handle entries should NOT be removed from in progress map' - 0 * mockModuleSyncStartedOnCmHandles.remove('cmhandle2') - and: '1st and 3rd cm-handle deletes successfully' - with(response.removedCmHandles[0]) { - assert it.status == Status.SUCCESS - assert it.cmHandle == 'cmhandle1' - } - with(response.removedCmHandles[2]) { - assert it.status == Status.SUCCESS - assert it.cmHandle == 'cmhandle3' - } - and: '2nd cm-handle deletion fails' - with(response.removedCmHandles[1]) { - assert it.status == Status.FAILURE - assert it.ncmpResponseStatus == UNKNOWN_ERROR - assert it.errorText == 'Failed' - assert it.cmHandle == 'cmhandle2' - } - and: 'the cmHandle state is updated to DELETED for 1st and 3rd' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch({ - assert it.size() == 2 - assert it.every { entry -> entry.value == CmHandleState.DELETED } - }) - and: 'No cm handles state updates for "upgraded cm handles"' - 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:]) - - } - - def 'Remove CmHandle Error Handling: Schema Set Deletion failed'() { - given: 'a registration' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', - removedCmHandles: ['cmhandle']) - and: 'schema set batch deletion failed with unknown error' - mockInventoryPersistence.deleteSchemaSetsWithCascade(_) >> { throw new RuntimeException('Failed') } - and: 'schema set single deletion failed with unknown error' - mockInventoryPersistence.deleteSchemaSetWithCascade(_) >> { throw new RuntimeException('Failed') } - when: 'registration is updated to delete cmhandle' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'no exception is thrown' - noExceptionThrown() - and: 'cm-handle is not deleted' - 0 * mockInventoryPersistence.deleteDataNodes(_) - and: 'the cmHandle state is not updated to "DELETED"' - 0 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([yangModelCmHandle: CmHandleState.DELETED]) - and: 'a failure response is received' - assert response.removedCmHandles.size() == 1 - with(response.removedCmHandles[0]) { - assert it.status == Status.FAILURE - assert it.cmHandle == 'cmhandle' - assert it.errorText == 'Failed' - assert it.ncmpResponseStatus == UNKNOWN_ERROR - } - } - - def 'Remove CmHandle Error Handling: #scenario'() { - given: 'a registration' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', - removedCmHandles: ['cmhandle']) - and: 'cm-handle deletion fails on batch' - mockInventoryPersistence.deleteDataNodes(_) >> { throw deleteListElementException } - and: 'cm-handle deletion fails on individual delete' - mockInventoryPersistence.deleteDataNode(_) >> { throw deleteListElementException } - when: 'registration is updated to delete cmhandle' - def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) - then: 'a failure response is received' - assert response.removedCmHandles.size() == 1 - with(response.removedCmHandles[0]) { - assert it.status == Status.FAILURE - assert it.cmHandle == 'cmhandle' - assert it.ncmpResponseStatus == expectedError - assert it.errorText == expectedErrorText - } - and: 'the cm handle state is not updated to "DELETED"' - 0 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_, CmHandleState.DELETED) - where: - scenario | cmHandleId | deleteListElementException || expectedError | expectedErrorText - 'cm-handle does not exist' | 'cmhandle' | new DataNodeNotFoundException('', '', '') || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found' - 'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException('', '') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id' - 'an unexpected exception' | '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 deleted file mode 100644 index d91c79d33d..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ /dev/null @@ -1,413 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation - * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2021-2022 Bell Canada - * Modifications Copyright (C) 2023 TechMahindra Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl - -import reactor.core.publisher.Mono - -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR -import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL -import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL -import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING -import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE -import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE - -import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse -import org.onap.cps.ncmp.api.models.CmResourceAddress -import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker -import com.hazelcast.map.IMap -import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService -import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState -import org.onap.cps.ncmp.api.impl.inventory.CompositeState -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence -import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory -import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState -import org.onap.cps.ncmp.api.models.DataOperationDefinition -import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters -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.ncmp.api.models.DataOperationRequest -import org.onap.cps.spi.exceptions.CpsException -import org.onap.cps.spi.model.ConditionProperties -import java.util.stream.Collectors -import org.onap.cps.utils.JsonObjectMapper -import com.fasterxml.jackson.databind.ObjectMapper -import org.onap.cps.api.CpsDataService -import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations -import org.onap.cps.spi.FetchDescendantsOption -import org.onap.cps.spi.model.DataNode -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import spock.lang.Specification - -class NetworkCmProxyDataServiceImplSpec extends Specification { - - def mockCpsDataService = Mock(CpsDataService) - def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) - def mockDmiDataOperations = Mock(DmiDataOperations) - def nullNetworkCmProxyDataServicePropertyHandler = null - def mockInventoryPersistence = Mock(InventoryPersistence) - def mockCmHandleQueries = Mock(CmHandleQueries) - def mockDmiPluginRegistration = Mock(DmiPluginRegistration) - def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService) - def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler) - def stubModuleSyncStartedOnCmHandles = Stub(IMap) - def stubTrustLevelPerDmiPlugin = Stub(Map) - def mockTrustLevelManager = Mock(TrustLevelManager) - def mockAlternateIdChecker = Mock(AlternateIdChecker) - - def NO_TOPIC = null - def NO_REQUEST_ID = null - def NO_AUTH_HEADER = null - def OPTIONS_PARAM = '(a=1,b=2)' - def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id') - - def objectUnderTest = new NetworkCmProxyDataServiceImpl( - spiedJsonObjectMapper, - mockDmiDataOperations, - nullNetworkCmProxyDataServicePropertyHandler, - mockInventoryPersistence, - mockCmHandleQueries, - mockCpsCmHandlerQueryService, - mockLcmEventsCmHandleStateHandler, - mockCpsDataService, - stubModuleSyncStartedOnCmHandles, - stubTrustLevelPerDmiPlugin, - mockTrustLevelManager, - mockAlternateIdChecker) - - def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']" - - def dataNode = [new DataNode(leaves: ['id': 'some-cm-handle', 'dmi-service-name': 'testDmiService'])] - - def 'Write resource data for pass-through running from DMI using POST.'() { - given: 'cpsDataService returns valid datanode' - mockDataNode() - when: 'write resource data is called' - objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', - 'testResourceId', CREATE, - '{some-json}', 'application/json', null) - then: 'DMI called with correct data' - 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId', - CREATE, '{some-json}', 'application/json', null) - >> { new ResponseEntity<>(HttpStatus.CREATED) } - } - - def 'Get resource data from DMI.'() { - given: 'cpsDataService returns valid data node' - mockDataNode() - and: 'some cm resource address' - def cmResourceAddress = new CmResourceAddress('some datastore', 'some CM Handle', 'some resource Id') - and: 'get resource data from DMI is called' - mockDmiDataOperations.getResourceDataFromDmi(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >> - Mono.just(new ResponseEntity<>('dmi-response', HttpStatus.OK)) - when: 'get resource data operational for the given cm resource address is called' - def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER).block() - then: 'DMI returns a json response' - assert response == 'dmi-response' - } - - def 'Get resource data for operational (cached) data.'() { - given: 'CPS Data service returns some object(s)' - mockCpsDataService.getDataNodes(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) >> ['First Object', 'other Object'] - and: 'a cm resource address for the same datastore, cm handle and resource id' - def cmResourceAddress = new CmResourceAddress(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId') - when: 'get resource data is called' - def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, FetchDescendantsOption.OMIT_DESCENDANTS) - then: 'get resource data returns teh first object from the data service' - assert response == 'First Object' - } - - def 'Execute (async) data operation for #datastoreName from DMI.'() { - given: 'cpsDataService returns valid data node' - def dataOperationRequest = getDataOperationRequest(datastoreName) - when: 'request resource data for data operation is called' - objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER) - then: 'request resource data for data operation returns expected response' - 1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER) - where: 'the following data stores are used' - datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName] - } - - def 'Getting Yang Resources.'() { - when: 'yang resources is called' - objectUnderTest.getYangResourcesModuleReferences('some-cm-handle') - then: 'CPS module services is invoked for the correct dataspace and cm handle' - 1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle') - } - - def 'Get a cm handle.'() { - given: 'the system returns a yang modelled cm handle' - def dmiServiceName = 'some service name' - def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, - lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(), - lastUpdateTime: 'some-timestamp', - dataSyncEnabled: false, - dataStores: dataStores()) - def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')] - def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')] - def moduleSetTag = 'some-module-set-tag' - def alternateId = 'some-alternate-id' - def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName, - dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState, - moduleSetTag: moduleSetTag, alternateId: alternateId) - 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle - when: 'getting cm handle details for a given cm handle id from ncmp service' - def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle') - then: 'the result is a ncmpServiceCmHandle' - result.class == NcmpServiceCmHandle.class - and: 'the cm handle contains the cm handle id' - result.cmHandleId == 'some-cm-handle' - and: 'the cm handle contains the alternate id' - result.alternateId == 'some-alternate-id' - and: 'the cm handle contains the module-set-tag' - result.moduleSetTag == 'some-module-set-tag' - and: 'the cm handle contains the DMI Properties' - result.dmiProperties ==[ Book:'Romance Novel' ] - and: 'the cm handle contains the public Properties' - result.publicProperties == [ "Public Book":'Public Romance Novel' ] - and: 'the cm handle contains the cm handle composite state' - result.compositeState == compositeState - } - - def 'Get cm handle public properties'() { - given: 'a yang modelled cm handle' - def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')] - def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')] - def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties) - and: 'the system returns this yang modelled cm handle' - 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle - when: 'getting cm handle public properties for a given cm handle id from ncmp service' - def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle') - then: 'the result returns the correct data' - result == [ 'public prop' : 'some public prop' ] - } - - def 'Execute cm handle id search for inventory'() { - given: 'a ConditionApiProperties object' - def conditionProperties = new ConditionProperties() - conditionProperties.conditionName = 'hasAllProperties' - conditionProperties.conditionParameters = [ [ 'some-key' : 'some-value' ] ] - def conditionServiceProps = new CmHandleQueryServiceParameters() - conditionServiceProps.cmHandleQueryParameters = [conditionProperties] as List - and: 'the system returns an set of cmHandle ids' - mockCpsCmHandlerQueryService.queryCmHandleIdsForInventory(*_) >> [ 'cmHandle1', 'cmHandle2' ] - when: 'getting cm handle id set for a given dmi property' - def result = objectUnderTest.executeCmHandleIdSearchForInventory(conditionServiceProps) - then: 'the result returns the correct 2 elements' - assert result.size() == 2 - assert result.contains('cmHandle1') - assert result.contains('cmHandle2') - } - - def 'Get cm handle composite state'() { - given: 'a yang modelled cm handle' - def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, - lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(), - lastUpdateTime: 'some-timestamp', - dataSyncEnabled: false, - dataStores: dataStores()) - def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')] - def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')] - def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState) - and: 'the system returns this yang modelled cm handle' - 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle - when: 'getting cm handle composite state for a given cm handle id from ncmp service' - def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle') - then: 'the result returns the correct data' - result == compositeState - } - - def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() { - given: 'cpsDataService returns valid datanode' - mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode - when: 'get resource data is called' - objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', - 'testResourceId', UPDATE, - '{some-json}', 'application/json', NO_AUTH_HEADER) - then: 'DMI called with correct data' - 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId', - UPDATE, '{some-json}', 'application/json', NO_AUTH_HEADER) - >> { new ResponseEntity<>(HttpStatus.OK) } - } - - def 'Verify modules and create anchor params.'() { - given: 'dmi plugin registration return created cm handles' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1', - dmiDataPlugin: 'service2') - dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] - mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle] - and: 'no rejected cm handles because of alternate ids' - mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> [] - when: 'parse and create cm handle in dmi registration then sync module' - mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id'] - objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse()) - then: 'system persists the cm handle state' - 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> { - args -> { - def cmHandleStatePerCmHandle = (args[0] as Collection) - cmHandleStatePerCmHandle.each { - assert it.id == 'test-cm-handle-id' - } - } - } - } - - def 'Execute cm handle id search'() { - given: 'valid CmHandleQueryApiParameters input' - def cmHandleQueryApiParameters = new CmHandleQueryApiParameters() - def conditionApiProperties = new ConditionApiProperties() - conditionApiProperties.conditionName = 'hasAllModules' - conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']] - cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties] - and: 'query cm handle method return with a data node list' - mockCpsCmHandlerQueryService.queryCmHandleIds( - spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class)) - >> ['cm-handle-id-1'] - when: 'execute cm handle search is called' - def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters) - then: 'result is the same collection as returned by the CPS Data Service' - assert result == ['cm-handle-id-1'] - } - - def 'Getting module definitions by module'() { - when: 'get module definitions is performed with module name' - objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04') - then: 'ncmp inventory persistence service is invoked once with correct parameters' - 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04') - } - - def 'Getting module definitions by cm handle id'() { - when: 'get module definitions is performed with cm handle id' - objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle') - then: 'ncmp inventory persistence service is invoked once with correct parameter' - 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle') - } - - def 'Execute cm handle search'() { - given: 'valid CmHandleQueryApiParameters input' - def cmHandleQueryApiParameters = new CmHandleQueryApiParameters() - def conditionApiProperties = new ConditionApiProperties() - conditionApiProperties.conditionName = 'hasAllModules' - conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']] - cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties] - and: 'query cm handle method return with a data node list' - mockCpsCmHandlerQueryService.queryCmHandles( - spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class)) - >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')] - when: 'execute cm handle search is called' - def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters) - then: 'result is the same collection as returned by the CPS Data Service' - assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set - } - - def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is #scenario'() { - given: 'an existing cm handle composite state' - def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag, - dataStores: CompositeState.DataStores.builder() - .operationalDataStore(CompositeState.Operational.builder() - .dataStoreSyncState(initialDataSyncState) - .build()).build()) - and: 'get cm handle state returns the composite state for the given cm handle id' - mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState - when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag' - objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag) - then: 'the data sync enabled flag is set to #dataSyncEnabled' - compositeState.dataSyncEnabled == dataSyncEnabledFlag - and: 'the data store sync state is set to #expectedDataStoreSyncState' - compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState - and: 'the cps data service to delete data nodes is invoked the expected number of times' - deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _) - and: 'the inventory persistence service to update node leaves is called with the correct values' - saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState) - where: 'the following data sync enabled flag is used' - scenario | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState || expectedDataStoreSyncState | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations - 'enabled' | true | false | DataStoreSyncState.NONE_REQUESTED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 1 - 'disabled' | false | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 0 | 1 - 'disabled where sync-state is currently SYNCHRONIZED' | false | true | DataStoreSyncState.SYNCHRONIZED || DataStoreSyncState.NONE_REQUESTED | 1 | 1 - 'is set to existing flag state' | true | true | DataStoreSyncState.UNSYNCHRONIZED || DataStoreSyncState.UNSYNCHRONIZED | 0 | 0 - } - - def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () { - given: 'a cm handle composite state' - def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false) - and: 'get cm handle state returns the composite state for the given cm handle id' - mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState - when: 'set data sync enabled is called with the data sync enabled flag set to true' - objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true) - then: 'the expected exception is thrown' - thrown(CpsException) - and: 'the inventory persistence service to update node leaves is not invoked' - 0 * mockInventoryPersistence.saveCmHandleState(_, _) - } - - def 'Get all cm handle IDs by DMI plugin identifier.' () { - given: 'cm handle queries service returns cm handles' - 1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2'] - when: 'cm handle Ids are requested with dmi plugin identifier' - def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') - then: 'the result size is correct' - assert result.size() == 2 - and: 'the result returns the correct details' - assert result.containsAll('cm-handle-1','cm-handle-2') - } - - def dataStores() { - CompositeState.DataStores.builder() - .operationalDataStore(CompositeState.Operational.builder() - .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED) - .lastSyncTime('some-timestamp').build()).build() - } - - def mockDataNode() { - mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode - } - - def getDataOperationRequest(datastore) { - def dataOperationRequest = new DataOperationRequest() - def dataOperationDefinitions = new ArrayList() - dataOperationDefinitions.add(getDataOperationDefinition(datastore)) - dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions) - return dataOperationRequest - } - - def getDataOperationDefinition(datastore) { - def dataOperationDefinition = new DataOperationDefinition() - dataOperationDefinition.setOperation("read") - dataOperationDefinition.setOperationId("operational-12") - dataOperationDefinition.setDatastore(datastore) - def targetIds = new ArrayList() - targetIds.add("some-cm-handle") - dataOperationDefinition.setCmHandleIds(targetIds) - return dataOperationDefinition - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy deleted file mode 100644 index b0024b19b8..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy +++ /dev/null @@ -1,285 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 Nordix Foundation - * Modifications Copyright (C) 2022 Bell Canada - * Modifications Copyright (C) 2024 TechMahindra Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl - -import ch.qos.logback.classic.Level -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.read.ListAppender -import com.fasterxml.jackson.databind.ObjectMapper -import org.onap.cps.api.CpsDataService -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence -import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle -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.ContentType -import org.onap.cps.utils.JsonObjectMapper -import org.slf4j.LoggerFactory -import spock.lang.Specification - -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND -import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID -import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR -import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status - -class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { - - def mockInventoryPersistence = Mock(InventoryPersistence) - def mockCpsDataService = Mock(CpsDataService) - def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) - def mockAlternateIdChecker = Mock(AlternateIdChecker) - - def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockAlternateIdChecker) - def logger = Spy(ListAppender) - - void setup() { - def setupLogger = ((Logger) LoggerFactory.getLogger(NetworkCmProxyDataServicePropertyHandler.class)) - setupLogger.addAppender(logger) - setupLogger.setLevel(Level.DEBUG) - logger.start() - // Always accept all alternate IDs - mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> [] - } - - void cleanup() { - ((Logger) LoggerFactory.getLogger(NetworkCmProxyDataServicePropertyHandler.class)).detachAndStopAllAppenders() - } - - def static cmHandleId = 'myHandle1' - def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']" - - def static propertyDataNodes = [new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp1']").withLeaves(['name': 'additionalProp1', 'value': 'additionalValue1']).build(), - new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp2']").withLeaves(['name': 'additionalProp2', 'value': 'additionalValue2']).build(), - new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/public-properties[@name='publicProp3']").withLeaves(['name': 'publicProp3', 'value': 'publicValue3']).build(), - new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/public-properties[@name='publicProp4']").withLeaves(['name': 'publicProp4', 'value': 'publicValue4']).build()] - def static cmHandleDataNodeAsCollection = [new DataNode(xpath: cmHandleXpath, childDataNodes: propertyDataNodes, leaves: ['id': cmHandleId])] - - def 'Update CM Handle Public Properties: #scenario'() { - given: 'the CPS service return a CM handle' - mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> cmHandleDataNodeAsCollection - and: 'an update cm handle request with public properties updates' - def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: updatedPublicProperties)] - when: 'update data node leaves is called with the update request' - objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) - then: 'the replace list method is called with correct params' - 1 * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) >> { args -> - { - assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size() - assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate)) - } - } - where: 'following public properties updates are made' - scenario | updatedPublicProperties || expectedPropertiesAfterUpdate - 'property added' | ['newPubProp1': 'pub-val'] || [['publicProp3': 'publicValue3'], ['publicProp4': 'publicValue4'], ['newPubProp1': 'pub-val']] - 'property updated' | ['publicProp4': 'newPubVal'] || [['publicProp3': 'publicValue3'], ['publicProp4': 'newPubVal']] - 'property removed' | ['publicProp4': null] || [['publicProp3': 'publicValue3']] - 'property ignored(value is null)' | ['pub-prop': null] || [['publicProp3': 'publicValue3'], ['publicProp4': 'publicValue4']] - } - - def 'Update DMI Properties: #scenario'() { - given: 'the CPS service return a CM handle' - mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> cmHandleDataNodeAsCollection - and: 'an update cm handle request with DMI properties updates' - def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: updatedDmiProperties)] - when: 'update data node leaves is called with the update request' - objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) - then: 'replace list method should is called with correct params' - expectedCallsToReplaceMethod * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) >> { args -> - { - assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size() - assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate)) - } - } - where: 'following DMI properties updates are made' - scenario | updatedDmiProperties || expectedPropertiesAfterUpdate | expectedCallsToReplaceMethod - 'property added' | ['newAdditionalProp1': 'add-value'] || [['additionalProp1': 'additionalValue1'], ['additionalProp2': 'additionalValue2'], ['newAdditionalProp1': 'add-value']] | 1 - 'property updated' | ['additionalProp1': 'newValue'] || [['additionalProp2': 'additionalValue2'], ['additionalProp1': 'newValue']] | 1 - 'property removed' | ['additionalProp1': null] || [['additionalProp2': 'additionalValue2']] | 1 - 'property ignored(value is null)' | ['new-prop': null] || [['additionalProp1': 'additionalValue1'], ['additionalProp2': 'additionalValue2']] | 1 - 'no property changes' | [:] || [['additionalProp1': 'additionalValue1'], ['additionalProp2': 'additionalValue2']] | 0 - } - - def 'Update CM Handle Properties, remove all properties: #scenario'() { - given: 'the CPS service return a CM handle' - def cmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId], childDataNodes: originalPropertyDataNodes) - mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [cmHandleDataNode] - and: 'an update cm handle request that removes all public properties(existing and non-existing)' - def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp3': null, 'publicProp4': null])] - when: 'update data node leaves is called with the update request' - objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) - then: 'the replace list method is not called' - 0 * mockInventoryPersistence.replaceListContent(*_) - then: 'delete data node will be called for any existing property' - expectedCallsToDeleteDataNode * mockInventoryPersistence.deleteDataNode(_) >> { arg -> - { - assert arg[0].contains("@name='publicProp") - } - } - where: 'following public properties updates are made' - scenario | originalPropertyDataNodes || expectedCallsToDeleteDataNode - '2 original properties, both removed' | propertyDataNodes || 2 - 'no original properties' | [] || 0 - } - - def '#scenario error leads to #exception when we try to update cmHandle'() { - given: 'cm handles request' - def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: [:], dmiProperties: [:])] - and: 'data node cannot be found' - mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(*_) >> { throw exception } - when: 'update data node leaves is called using correct parameters' - def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) - then: 'one failed registration response' - response.size() == 1 - and: 'it has expected error details' - with(response.get(0)) { - assert it.status == Status.FAILURE - assert it.cmHandle == cmHandleId - assert it.ncmpResponseStatus == expectedError - assert it.errorText == expectedErrorText - } - where: - scenario | cmHandleId | exception || expectedError | expectedErrorText - 'Cm Handle does not exist' | 'cmHandleId' | new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found' - 'Unknown' | 'cmHandleId' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed' - 'Invalid cm handle id' | 'cmHandleId with spaces' | new DataValidationException('Name Validation Error.', cmHandleId + 'contains an invalid character') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id' - } - - def 'Multiple update operations in a single request'() { - given: 'cm handles request' - def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]), - new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]), - new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])] - and: 'data node can be found for 1st and 3rd cm-handle but not for 2nd cm-handle' - mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(*_) >> cmHandleDataNodeAsCollection >> { - throw new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) } >> cmHandleDataNodeAsCollection - when: 'update data node leaves is called using correct parameters' - def cmHandleResponseList = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) - then: 'response has 3 values' - cmHandleResponseList.size() == 3 - and: 'the 1st and 3rd requests were processed successfully' - with(cmHandleResponseList.get(0)) { - assert it.status == Status.SUCCESS - assert it.cmHandle == cmHandleId - } - with(cmHandleResponseList.get(2)) { - assert it.status == Status.SUCCESS - assert it.cmHandle == cmHandleId - } - and: 'the 2nd request failed with correct error code' - with(cmHandleResponseList.get(1)) { - assert it.status == Status.FAILURE - assert it.cmHandle == cmHandleId - assert it.ncmpResponseStatus == CM_HANDLES_NOT_FOUND - assert it.errorText == 'cm handle id(s) not found' - } - then: 'the replace list method is called twice' - 2 * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) - } - - def 'Update alternate id of existing CM Handle.'() { - given: 'cm handles request' - def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')] - and: 'a data node found' - def dataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId, 'alternate-id': 'alt-1']) - mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [dataNode] - when: 'cm handle properties is updated' - def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest) - then: 'the update is delegated to cps data service with correct parameters' - 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _, ContentType.JSON) >> - { args -> - assert args[3].contains('alt-1') - } - and: 'one successful registration response' - response.size() == 1 - and: 'the response shows success for the given cm handle id' - assert response[0].status == Status.SUCCESS - assert response[0].cmHandle == cmHandleId - } - - def 'Update with rejected alternate id.'() { - given: 'cm handles request' - def updatedNcmpServiceCmHandles = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')] - and: 'a data node found' - def dataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId, 'alternate-id': 'alt-1']) - mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [dataNode] - when: 'attempt to update the cm handle' - def response = objectUnderTest.updateCmHandleProperties(updatedNcmpServiceCmHandles) - then: 'the update is NOT delegated to cps data service' - 0 * mockCpsDataService.updateNodeLeaves(*_) - and: 'the alternate id checker rejects the given cm handle (override default setup behavior)' - mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> [cmHandleId] - and: 'the response shows a failure for the given cm handle id' - assert response[0].status == Status.FAILURE - assert response[0].cmHandle == cmHandleId - } - - def 'Update CM Handle data producer identifier from #scenario'() { - given: 'an existing cm handle with no data producer identifier' - DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId','data-producer-identifier': oldDataProducerIdentifier]) - and: 'an update request with a new data producer identifier' - def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dataProducerIdentifier: 'someDataProducerIdentifier') - when: 'data producer identifier updated' - objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle) - then: 'the update node leaves method is invoked once' - 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _, ContentType.JSON) >> { args -> - assert args[3].contains('someDataProducerIdentifier') - } - and: 'correct information is logged' - def lastLoggingEvent = logger.list[0] - assert lastLoggingEvent.level == Level.DEBUG - assert lastLoggingEvent.formattedMessage.contains('Updating data-producer-identifier') - where: 'the following scenarios are attempted' - scenario | oldDataProducerIdentifier - 'null to something' | null - 'blank to something' | '' - } - - def 'Update CM Handle data producer identifier from some data producer identifier to another data producer identifier'() { - given: 'an existing cm handle with a data producer identifier' - DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId', 'data-producer-identifier': 'someDataProducerIdentifier']) - and: 'an update request with a new data producer identifier' - def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dataProducerIdentifier: 'someNewDataProducerIdentifier') - when: 'update data producer identifier is called with the update request' - objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle) - then: 'the update node leaves method is not invoked' - 0 * mockCpsDataService.updateNodeLeaves(*_) - and: 'correct information is logged' - def lastLoggingEvent = logger.list[0] - assert lastLoggingEvent.level == Level.WARN - assert lastLoggingEvent.formattedMessage.contains('Unable to update dataProducerIdentifier') - } - - def convertToProperties(expectedPropertiesAfterUpdateAsMap) { - def properties = [].withDefault { [:] } - expectedPropertiesAfterUpdateAsMap.forEach(property -> - property.forEach((key, val) -> { - properties.add(['name': key, 'value': val]) - })) - return properties - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyFacadeSpec.groovy new file mode 100644 index 0000000000..f79e0ee2ef --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyFacadeSpec.groovy @@ -0,0 +1,108 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2021-2022 Bell Canada + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl + +import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations +import org.onap.cps.ncmp.api.models.CmResourceAddress +import org.onap.cps.ncmp.api.models.DataOperationRequest +import org.onap.cps.spi.model.DataNode +import reactor.core.publisher.Mono +import spock.lang.Specification + +import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL +import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL +import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING +import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE +import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE + +class NetworkCmProxyFacadeSpec extends Specification { + + def mockDmiDataOperations = Mock(DmiDataOperations) + def mockNcmpCachedResourceRequestHandler = Mock(NcmpCachedResourceRequestHandler) + def mockNcmpPassthroughResourceRequestHandler = Mock(NcmpPassthroughResourceRequestHandler) + + def objectUnderTest = new NetworkCmProxyFacade(mockNcmpCachedResourceRequestHandler, mockNcmpPassthroughResourceRequestHandler, mockDmiDataOperations) + + def NO_TOPIC = null + + def 'Execute Data Operation for CM Handles (delegation).'() { + given: 'a data operation request' + def dataOperationRequest = Mock(DataOperationRequest) + and: ' a response from the (mocked) pass-through request handler for the given parameters' + def responseFromHandler = [attr:'value'] + mockNcmpPassthroughResourceRequestHandler.executeAsynchronousRequest('topic', dataOperationRequest, 'authorization') >> responseFromHandler + expect: 'the response form the handler' + assert objectUnderTest.executeDataOperationForCmHandles('topic', dataOperationRequest, 'authorization') == responseFromHandler + } + + def 'Query Resource Data for cm handle (delegation).'() { + given: 'a response from the (mocked) cached data handler for the given parameters' + def responseFromHandler = [Mock(DataNode)] + mockNcmpCachedResourceRequestHandler.executeRequest('ch-1', 'some cps path', true) >> responseFromHandler + expect: 'the response form the handler' + assert objectUnderTest.queryResourceDataForCmHandle('ch-1','some cps path',true) == responseFromHandler + } + + def 'Choosing Data Request Handler.'() { + expect: '(a mock of) #expectedHandler' + assert objectUnderTest.getNcmpDatastoreRequestHandler(datastore.datastoreName).class.name.startsWith(expectedHandler.name) + where: + datastore || expectedHandler + OPERATIONAL || NcmpCachedResourceRequestHandler.class + PASSTHROUGH_RUNNING || NcmpPassthroughResourceRequestHandler.class + PASSTHROUGH_OPERATIONAL || NcmpPassthroughResourceRequestHandler.class + } + + def 'Write resource data for pass-through running from DMI using POST (delegation).'() { + when: 'write resource data is called' + objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', + 'testResourceId', CREATE, + '{some-json}', 'application/json', null) + then: 'DMI called with correct data' + 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId', CREATE, '{some-json}', 'application/json', null) + } + + def 'Get resource data from DMI (delegation).'() { + given: 'a cm resource address for datastore operational' + def cmResourceAddress = new CmResourceAddress('ncmp-datastore:operational', 'some CM Handle', 'some resource Id') + and: 'get resource data from DMI is called' + mockNcmpCachedResourceRequestHandler.executeRequest(cmResourceAddress, 'options', NO_TOPIC, false, 'authorization') >> + Mono.just('dmi response') + when: 'get resource data operational for the given cm resource address is called' + def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, 'options', NO_TOPIC, false, 'authorization').block() + then: 'DMI returns a json response' + assert response == 'dmi response' + } + + def 'Update resource data for pass-through running from dmi (delegation).'() { + when: 'get resource data is called' + objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', + 'testResourceId', UPDATE, + '{some-json}', 'application/json', 'authorization') + then: 'DMI called with correct data' + 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId', UPDATE, '{some-json}', 'application/json', 'authorization') + } + + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyInventoryFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyInventoryFacadeSpec.groovy new file mode 100644 index 0000000000..f704dbb3d6 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyInventoryFacadeSpec.groovy @@ -0,0 +1,226 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2021-2022 Bell Canada + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.ncmp.api.ParameterizedCmHandleQueryService +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService +import org.onap.cps.ncmp.api.impl.inventory.CmHandleState +import org.onap.cps.ncmp.api.impl.inventory.CompositeState +import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence +import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle +import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters +import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters +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.model.ConditionProperties +import org.onap.cps.utils.JsonObjectMapper +import spock.lang.Specification + +import java.util.stream.Collectors + +class NetworkCmProxyInventoryFacadeSpec extends Specification { + + def mockCmHandleRegistrationService = Mock(CmHandleRegistrationService) + def mockCmHandleQueryService = Mock(CmHandleQueryService) + def mockParameterizedCmHandleQueryService = Mock(ParameterizedCmHandleQueryService) + def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) + def mockInventoryPersistence = Mock(InventoryPersistence) + + def objectUnderTest = new NetworkCmProxyInventoryFacade(mockCmHandleRegistrationService, mockCmHandleQueryService, mockParameterizedCmHandleQueryService, mockInventoryPersistence, spiedJsonObjectMapper) + + def 'Update DMI Registration'() { + given: 'an (updated) dmi plugin registration' + def dmiPluginRegistration = Mock(DmiPluginRegistration) + when: 'the registration is submitted ' + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'the call is delegated to the cm handle registration service' + 1 * mockCmHandleRegistrationService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + } + + def 'Execute cm handle id search for inventory'() { + given: 'a ConditionApiProperties object' + def conditionProperties = new ConditionProperties() + conditionProperties.conditionName = 'hasAllProperties' + conditionProperties.conditionParameters = [ [ 'some-key' : 'some-value' ] ] + def cmHandleQueryServiceParameters = new CmHandleQueryServiceParameters() + cmHandleQueryServiceParameters.cmHandleQueryParameters = [conditionProperties] as List + and: 'the system returns an set of cmHandle ids' + mockParameterizedCmHandleQueryService.queryCmHandleIdsForInventory(*_) >> [ 'cmHandle1', 'cmHandle2' ] + when: 'executing the search' + def result = objectUnderTest.executeParameterizedCmHandleIdSearch(cmHandleQueryServiceParameters) + then: 'the result returns the correct 2 elements' + assert result.size() == 2 + assert result.contains('cmHandle1') + assert result.contains('cmHandle2') + } + + def 'Get all cm handle IDs by DMI plugin identifier.' () { + given: 'cm handle queries service returns cm handles' + 1 * mockCmHandleQueryService.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2'] + when: 'cm handle Ids are requested with dmi plugin identifier' + def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') + then: 'the result size is correct' + assert result.size() == 2 + and: 'the result returns the correct details' + assert result.containsAll('cm-handle-1','cm-handle-2') + } + + def 'Getting Yang Resources.'() { + when: 'yang resources is called' + objectUnderTest.getYangResourcesModuleReferences('some-cm-handle') + then: 'CPS module services is invoked for the correct dataspace and cm handle' + 1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle') + } + + def 'Get a cm handle.'() { + given: 'the system returns a yang modelled cm handle' + def dmiServiceName = 'some service name' + def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, + lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(), + lastUpdateTime: 'some-timestamp', + dataSyncEnabled: false, + dataStores: dataStores()) + def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')] + def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')] + def moduleSetTag = 'some-module-set-tag' + def alternateId = 'some-alternate-id' + def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName, + dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState, + moduleSetTag: moduleSetTag, alternateId: alternateId) + 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle + when: 'getting cm handle details for a given cm handle id from ncmp service' + def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle') + then: 'the result is a ncmpServiceCmHandle' + result.class == NcmpServiceCmHandle.class + and: 'the cm handle contains the cm handle id' + result.cmHandleId == 'some-cm-handle' + and: 'the cm handle contains the alternate id' + result.alternateId == 'some-alternate-id' + and: 'the cm handle contains the module-set-tag' + result.moduleSetTag == 'some-module-set-tag' + and: 'the cm handle contains the DMI Properties' + result.dmiProperties ==[ Book:'Romance Novel' ] + and: 'the cm handle contains the public Properties' + result.publicProperties == [ "Public Book":'Public Romance Novel' ] + and: 'the cm handle contains the cm handle composite state' + result.compositeState == compositeState + } + + def 'Get cm handle public properties'() { + given: 'a yang modelled cm handle' + def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')] + def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')] + def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties) + and: 'the system returns this yang modelled cm handle' + 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle + when: 'getting cm handle public properties for a given cm handle id from ncmp service' + def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle') + then: 'the result returns the correct data' + result == [ 'public prop' : 'some public prop' ] + } + + def 'Get cm handle composite state'() { + given: 'a yang modelled cm handle' + def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, + lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(), + lastUpdateTime: 'some-timestamp', + dataSyncEnabled: false, + dataStores: dataStores()) + def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')] + def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')] + def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState) + and: 'the system returns this yang modelled cm handle' + 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle + when: 'getting cm handle composite state for a given cm handle id from ncmp service' + def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle') + then: 'the result returns the correct data' + result == compositeState + } + + def 'Execute cm handle id search'() { + given: 'valid CmHandleQueryApiParameters input' + def cmHandleQueryApiParameters = new CmHandleQueryApiParameters() + def conditionApiProperties = new ConditionApiProperties() + conditionApiProperties.conditionName = 'hasAllModules' + conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']] + cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties] + and: 'query cm handle method return with a data node list' + mockParameterizedCmHandleQueryService.queryCmHandleIds( + spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class)) + >> ['cm-handle-id-1'] + when: 'execute cm handle search is called' + def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters) + then: 'result is the same collection as returned by the CPS Data Service' + assert result == ['cm-handle-id-1'] + } + + def 'Getting module definitions by module'() { + when: 'get module definitions is performed with module name' + objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04') + then: 'ncmp inventory persistence service is invoked once with correct parameters' + 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04') + } + + def 'Getting module definitions by cm handle id'() { + when: 'get module definitions is performed with cm handle id' + objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle') + then: 'ncmp inventory persistence service is invoked once with correct parameter' + 1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle') + } + + def 'Execute cm handle search'() { + given: 'valid CmHandleQueryApiParameters input' + def cmHandleQueryApiParameters = new CmHandleQueryApiParameters() + def conditionApiProperties = new ConditionApiProperties() + conditionApiProperties.conditionName = 'hasAllModules' + conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']] + cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties] + and: 'query cm handle method return with a data node list' + mockParameterizedCmHandleQueryService.queryCmHandles( + spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class)) + >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')] + when: 'execute cm handle search is called' + def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters) + then: 'result is the same collection as returned by the CPS Data Service' + assert result.stream().map(cmHandle -> cmHandle.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set + } + + def 'Set Cm Handle Data Sync flag.'() { + when: 'setting data sync enabled flag' + objectUnderTest.setDataSyncEnabled('ch-1',true) + then: 'call is delegated to the cm handle registration service' + mockCmHandleRegistrationService.setDataSyncEnabled('ch-1', true) + } + + def dataStores() { + CompositeState.DataStores.builder() + .operationalDataStore(CompositeState.Operational.builder() + .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED) + .lastSyncTime('some-timestamp').build()).build() + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/ParameterizedCmHandleQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/ParameterizedCmHandleQueryServiceSpec.groovy new file mode 100644 index 0000000000..a928a0432c --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/ParameterizedCmHandleQueryServiceSpec.groovy @@ -0,0 +1,222 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl + +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT + +import org.onap.cps.cpspath.parser.PathParsingException +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence +import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle +import org.onap.cps.spi.FetchDescendantsOption +import org.onap.cps.spi.exceptions.DataInUseException +import org.onap.cps.spi.exceptions.DataValidationException +import org.onap.cps.spi.model.ConditionProperties +import org.onap.cps.spi.model.DataNode +import spock.lang.Specification + +class ParameterizedCmHandleQueryServiceSpec extends Specification { + + def cmHandleQueries = Mock(CmHandleQueryService) + def partiallyMockedCmHandleQueries = Spy(CmHandleQueryService) + def mockInventoryPersistence = Mock(InventoryPersistence) + + def dmiRegistry = new DataNode(xpath: NCMP_DMI_REGISTRY_PARENT, childDataNodes: createDataNodeList(['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4'])) + + def objectUnderTest = new ParameterizedCmHandleQueryServiceImpl(cmHandleQueries, mockInventoryPersistence) + def objectUnderTestWithPartiallyMockedQueries = new ParameterizedCmHandleQueryServiceImpl(partiallyMockedCmHandleQueries, mockInventoryPersistence) + + def 'Query cm handle ids with cpsPath.'() { + given: 'a cmHandleWithCpsPath condition property' + def cmHandleQueryParameters = new CmHandleQueryServiceParameters() + def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']]) + cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) + and: 'the query get the cm handle datanodes excluding all descendants returns a datanode' + cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])] + when: 'the query is executed for cm handle ids' + def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) + then: 'the correct expected cm handles ids are returned' + assert result == ['some-cmhandle-id'] as Set + } + + def 'Cm handle ids query with error: #scenario.'() { + given: 'a cmHandleWithCpsPath condition property' + def cmHandleQueryParameters = new CmHandleQueryServiceParameters() + def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']]) + cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) + and: 'cmHandleQueries throws a path parsing exception' + cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> { throw thrownException } + when: 'the query is executed for cm handle ids' + objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) + then: 'a data validation exception is thrown' + thrown(expectedException) + where: 'the following data is used' + scenario | thrownException || expectedException + 'PathParsingException' | new PathParsingException('some message', 'some details') || DataValidationException + 'any other Exception' | new DataInUseException('some message', 'some details') || DataInUseException + } + + def 'Cm handle ids cpsPath query for private properties (not allowed).'() { + given: 'a CpsPath condition property for private properties' + def cmHandleQueryParameters = new CmHandleQueryServiceParameters() + def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/additional-properties']]) + cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) + when: 'the query is executed for cm handle ids' + def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) + then: 'empty result is returned' + assert result.isEmpty() + } + + def 'Query cm handle ids with module names when #scenario from query.'() { + given: 'a modules condition property' + def cmHandleQueryParameters = new CmHandleQueryServiceParameters() + def conditionProperties = createConditionProperties('hasAllModules', [['moduleName': 'some-module-name']]) + cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) + when: 'the query is executed for cm handle ids' + def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) + then: 'the inventory service is called with the correct module names' + 1 * mockInventoryPersistence.getCmHandleIdsWithGivenModules(['some-module-name']) >> cmHandleIdsFromService + and: 'the correct expected cm handles ids are returned' + assert result.size() == cmHandleIdsFromService.size() + assert result.containsAll(cmHandleIdsFromService) + where: 'the following data is used' + scenario | cmHandleIdsFromService + 'One anchor returned' | ['some-cmhandle-id'] + 'No anchors are returned' | [] + } + + def 'Query cm handles with some trust level query parameters'() { + given: 'a trust level condition property' + def trustLevelQueryParameters = new CmHandleQueryServiceParameters() + def trustLevelConditionProperties = createConditionProperties('cmHandleWithTrustLevel', [['trustLevel': 'COMPLETE'] as Map]) + trustLevelQueryParameters.setCmHandleQueryParameters([trustLevelConditionProperties]) + when: 'the query is being executed' + objectUnderTest.queryCmHandleIds(trustLevelQueryParameters) + then: 'the query is being delegated to the cm handle query service with correct parameter' + 1 * cmHandleQueries.queryCmHandlesByTrustLevel(['trustLevel': 'COMPLETE'] as Map) + } + + def 'Query cm handle details with module names when #scenario from query.'() { + given: 'a modules condition property' + def cmHandleQueryParameters = new CmHandleQueryServiceParameters() + def conditionProperties = createConditionProperties('hasAllModules', [['moduleName': 'some-module-name']]) + cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) + when: 'the query is executed for cm handle ids' + def result = objectUnderTest.queryCmHandles(cmHandleQueryParameters) + then: 'the inventory service is called with the correct module names' + 1 * mockInventoryPersistence.getCmHandleIdsWithGivenModules(['some-module-name']) >> ['ch1'] + and: 'the inventory service is called with teh correct if and returns a yang model cm handle' + 1 * mockInventoryPersistence.getYangModelCmHandles(['ch1']) >> + [new YangModelCmHandle(id: 'abc', dmiProperties: [new YangModelCmHandle.Property('name','value')], publicProperties: [])] + and: 'the expected cm handle(s) are returned as NCMP Service cm handles' + assert result[0] instanceof NcmpServiceCmHandle + assert result.size() == 1 + assert result[0].dmiProperties == [name:'value'] + } + + def 'Query cm handle ids when the query is empty.'() { + given: 'We use an empty query' + def cmHandleQueryParameters = new CmHandleQueryServiceParameters() + and: 'the inventory persistence returns the dmi registry datanode with just ids' + mockInventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT, FetchDescendantsOption.DIRECT_CHILDREN_ONLY) >> [dmiRegistry] + when: 'the query is executed for both cm handle ids' + def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters) + then: 'the correct expected cm handles are returned' + assert result.containsAll('PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4') + } + + def 'Query cm handle details when the query is empty.'() { + given: 'We use an empty query' + def cmHandleQueryParameters = new CmHandleQueryServiceParameters() + and: 'the inventory persistence returns the dmi registry datanode with just ids' + mockInventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT) >> [dmiRegistry] + when: 'the query is executed for both cm handle details' + def result = objectUnderTest.queryCmHandles(cmHandleQueryParameters) + then: 'the correct cm handles are returned' + assert result.size() == 4 + assert result.cmHandleId.containsAll('PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4') + } + + def 'Query CMHandleId with #scenario.' () { + given: 'a query object created with #condition' + def cmHandleQueryParameters = new CmHandleQueryServiceParameters() + def conditionProperties = createConditionProperties(conditionName, [['some-key': 'some-value']]) + cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) + and: 'the inventoryPersistence returns different CmHandleIds' + partiallyMockedCmHandleQueries.queryCmHandlePublicProperties(*_) >> cmHandlesWithMatchingPublicProperties + partiallyMockedCmHandleQueries.queryCmHandleAdditionalProperties(*_) >> cmHandlesWithMatchingPrivateProperties + when: 'the query executed' + def result = objectUnderTestWithPartiallyMockedQueries.queryCmHandleIdsForInventory(cmHandleQueryParameters) + then: 'the expected number of results are returned.' + assert result.size() == expectedCmHandleIdsSize + where: 'the following data is used' + scenario | conditionName | cmHandlesWithMatchingPublicProperties | cmHandlesWithMatchingPrivateProperties || expectedCmHandleIdsSize + 'all properties, only public matching' | 'hasAllProperties' | ['h1', 'h2'] | null || 2 + 'all properties, no matching cm handles' | 'hasAllProperties' | [] | [] || 0 + 'additional properties, some matching cm handles' | 'hasAllAdditionalProperties' | [] | ['h1', 'h2'] || 2 + 'additional properties, no matching cm handles' | 'hasAllAdditionalProperties' | null | [] || 0 + } + + def 'Retrieve CMHandleIds by different DMI properties with #scenario.' () { + given: 'a query object created with dmi plugin as condition' + def cmHandleQueryParameters = new CmHandleQueryServiceParameters() + def conditionProperties = createConditionProperties('cmHandleWithDmiPlugin', [['some-key': 'some-value']]) + cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) + and: 'the inventoryPersistence returns different CmHandleIds' + partiallyMockedCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier(*_) >> cmHandleQueryResult + when: 'the query executed' + def result = objectUnderTestWithPartiallyMockedQueries.queryCmHandleIdsForInventory(cmHandleQueryParameters) + then: 'the expected number of results are returned.' + assert result.size() == expectedCmHandleIdsSize + where: 'the following data is used' + scenario | cmHandleQueryResult || expectedCmHandleIdsSize + 'some matches' | ['h1','h2'] || 2 + 'no matches' | [] || 0 + } + + def 'Combine two query results where #scenario.'() { + when: 'two query results in the form of a map of NcmpServiceCmHandles are combined into a single query result' + def result = objectUnderTest.combineCmHandleQueryResults(firstQuery, secondQuery) + then: 'the returned result is the same as the expected result' + result == expectedResult + where: + scenario | firstQuery | secondQuery || expectedResult + 'two queries with unique and non unique entries exist' | ['PNFDemo', 'PNFDemo2'] | ['PNFDemo', 'PNFDemo3'] || ['PNFDemo'] + 'the first query contains entries and second query is empty' | ['PNFDemo', 'PNFDemo2'] | [] || [] + 'the second query contains entries and first query is empty' | [] | ['PNFDemo', 'PNFDemo3'] || [] + 'the first query contains entries and second query is null' | ['PNFDemo', 'PNFDemo2'] | null || ['PNFDemo', 'PNFDemo2'] + 'the second query contains entries and first query is null' | null | ['PNFDemo', 'PNFDemo3'] || ['PNFDemo', 'PNFDemo3'] + 'both queries are empty' | [] | [] || [] + 'both queries are null' | null | null || null + } + + def createConditionProperties(String conditionName, List> conditionParameters) { + return new ConditionProperties(conditionName : conditionName, conditionParameters : conditionParameters) + } + + def static createDataNodeList(dataNodeIds) { + def dataNodes =[] + dataNodeIds.each{ dataNodes << new DataNode(xpath: "/dmi-registry/cm-handles[@id='${it}']", leaves: ['id':it]) } + return dataNodes + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy index 2c22127738..bb73c6879e 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy @@ -21,33 +21,32 @@ package org.onap.cps.ncmp.api.impl.client -import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR -import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE -import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH -import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ -import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA -import static org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE -import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING -import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA - import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.ObjectNode +import org.onap.cps.ncmp.api.impl.config.DmiProperties import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException import org.onap.cps.ncmp.api.impl.exception.InvalidDmiResourceUrlException -import org.onap.cps.ncmp.api.impl.config.DmiProperties import org.onap.cps.ncmp.utils.TestUtils import org.onap.cps.utils.JsonObjectMapper import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity -import org.springframework.web.client.HttpServerErrorException import org.springframework.web.reactive.function.client.WebClient -import org.springframework.web.reactive.function.client.WebClientResponseException import org.springframework.web.reactive.function.client.WebClientRequestException +import org.springframework.web.reactive.function.client.WebClientResponseException import reactor.core.publisher.Mono import spock.lang.Specification +import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR +import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE +import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH +import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ +import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA +import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.MODEL + class DmiRestClientSpec extends Specification { static final NO_AUTH_HEADER = null @@ -61,7 +60,6 @@ class DmiRestClientSpec extends Specification { def mockRequestBody = Mock(WebClient.RequestBodyUriSpec) def mockResponse = Mock(WebClient.ResponseSpec) - def responseBody = [message: 'Success'] def mockDmiProperties = Mock(DmiProperties) JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) @@ -75,34 +73,31 @@ class DmiRestClientSpec extends Specification { mockRequestBody.retrieve() >> mockResponse } - def 'DMI POST Operation with JSON for status #httpStatusCode'() { - given: 'the web client returns a valid response entity for the expected parameters' + def 'DMI POST Operation with JSON for DMI Data Service '() { + given: 'the Data web client returns a valid response entity for the expected parameters' mockDataServicesWebClient.post() >> mockRequestBody - mockResponse.toEntity(Object.class) >> Mono.just(new ResponseEntity<>(responseBody, httpStatusCode)) - when: 'POST operation is invoked' - def response = objectUnderTest.postOperationWithJsonData(DATA, '/my/url', 'some json', READ, NO_AUTH_HEADER) + mockResponse.toEntity(Object.class) >> Mono.just(new ResponseEntity<>('from Data service', HttpStatus.I_AM_A_TEAPOT)) + when: 'POST operation is invoked fro Data Service' + def response = objectUnderTest.synchronousPostOperationWithJsonData(DATA, '/my/url', 'some json', READ, NO_AUTH_HEADER) then: 'the output of the method is equal to the output from the test template' - assert response.statusCode == httpStatusCode - assert response.body == responseBody - where: 'the following status codes are used' - httpStatusCode << [HttpStatus.OK, HttpStatus.CREATED, HttpStatus.ACCEPTED] + assert response.statusCode == HttpStatus.I_AM_A_TEAPOT + assert response.body == 'from Data service' } - def 'Failing DMI POST operation for server error'() { - given: 'the web client throws an exception' - mockDataServicesWebClient.post() >> { throw new HttpServerErrorException(SERVICE_UNAVAILABLE, null, null, null) } - when: 'POST operation is invoked' - objectUnderTest.postOperationWithJsonData(DATA, '/some', 'some json', READ, NO_AUTH_HEADER) - then: 'a http client exception is thrown' - def thrown = thrown(DmiClientRequestException) - and: 'the exception has the relevant details from the error response' - thrown.ncmpResponseStatus.code == '102' - thrown.httpStatusCode == 503 + def 'DMI POST Operation with JSON for DMI Model Service '() { + given: 'the Model web client returns a valid response entity for the expected parameters' + mockModelServicesWebClient.post() >> mockRequestBody + mockResponse.toEntity(Object.class) >> Mono.just(new ResponseEntity<>('from Model service', HttpStatus.I_AM_A_TEAPOT)) + when: 'POST operation is invoked for Model Service' + def response = objectUnderTest.synchronousPostOperationWithJsonData(MODEL, '/my/url', 'some json', READ, NO_AUTH_HEADER) + then: 'the output of the method is equal to the output from the test template' + assert response.statusCode == HttpStatus.I_AM_A_TEAPOT + assert response.body == 'from Model service' } def 'Failing DMI POST operation due to invalid dmi resource url.'() { when: 'POST operation is invoked with invalid dmi resource url' - objectUnderTest.postOperationWithJsonData(DATA, '/invalid dmi url', null, null, NO_AUTH_HEADER) + objectUnderTest.synchronousPostOperationWithJsonData(DATA, '/invalid dmi url', null, null, NO_AUTH_HEADER) then: 'invalid dmi resource url exception is thrown' def thrown = thrown(InvalidDmiResourceUrlException) and: 'the exception has the relevant details from the error response' @@ -117,7 +112,7 @@ class DmiRestClientSpec extends Specification { mockDataServicesWebClient.post() >> mockRequestBody mockResponse.toEntity(Object.class) >> Mono.error(exceptionType) when: 'POST operation is invoked' - objectUnderTest.postOperationWithJsonData(DATA, '/my/url', 'some json', READ, NO_AUTH_HEADER) + objectUnderTest.synchronousPostOperationWithJsonData(DATA, '/my/url', 'some json', READ, NO_AUTH_HEADER) then: 'a http client exception is thrown' def thrown = thrown(DmiClientRequestException) and: 'the exception has the relevant details from the error response' @@ -176,4 +171,4 @@ class DmiRestClientSpec extends Specification { 'DMI basic auth disabled, with NCMP bearer token' | false | BEARER_AUTH_HEADER || BEARER_AUTH_HEADER 'DMI basic auth disabled, with NCMP basic auth' | false | BASIC_AUTH_HEADER || NO_AUTH_HEADER } -} \ No newline at end of file +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImplSpec.groovy deleted file mode 100644 index 2f9d264947..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImplSpec.groovy +++ /dev/null @@ -1,206 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation - * Modifications Copyright (C) 2023 TechMahindra Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.inventory - -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel -import org.onap.cps.spi.utils.CpsValidator -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT -import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS -import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS -import org.onap.cps.spi.CpsDataPersistenceService -import org.onap.cps.spi.model.DataNode -import spock.lang.Shared -import spock.lang.Specification - -class CmHandleQueriesImplSpec extends Specification { - - def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService) - - def trustLevelPerDmiPlugin = [:] - - def trustLevelPerCmHandle = [ 'PNFDemo': TrustLevel.COMPLETE, 'PNFDemo2': TrustLevel.NONE, 'PNFDemo4': TrustLevel.NONE ] - - def mockCpsValidator = Mock(CpsValidator) - - def objectUnderTest = new CmHandleQueriesImpl(mockCpsDataPersistenceService, trustLevelPerDmiPlugin, trustLevelPerCmHandle, mockCpsValidator) - - @Shared - def static sampleDataNodes = [new DataNode()] - - def dataNodeWithPrivateField = '//additional-properties[@name=\"Contact3\" and @value=\"newemailforstore3@bookstore.com\"]/ancestor::cm-handles' - - def static pnfDemo = createDataNode('PNFDemo') - def static pnfDemo2 = createDataNode('PNFDemo2') - def static pnfDemo3 = createDataNode('PNFDemo3') - def static pnfDemo4 = createDataNode('PNFDemo4') - def static pnfDemo5 = createDataNode('PNFDemo5') - - def 'Query CmHandles with public properties query pair.'() { - given: 'the DataNodes queried for a given cpsPath are returned from the persistence service.' - mockResponses() - when: 'a query on cmhandle public properties is performed with a public property pair' - def result = objectUnderTest.queryCmHandlePublicProperties(publicPropertyPairs) - then: 'the correct cm handle data objects are returned' - result.containsAll(expectedCmHandleIds) - result.size() == expectedCmHandleIds.size() - where: 'the following data is used' - scenario | publicPropertyPairs || expectedCmHandleIds - 'single property matches' | [Contact: 'newemailforstore@bookstore.com'] || ['PNFDemo', 'PNFDemo2', 'PNFDemo4'] - 'public property does not match' | [wont_match: 'wont_match'] || [] - '2 properties, only one match' | [Contact: 'newemailforstore@bookstore.com', Contact2: 'newemailforstore2@bookstore.com'] || ['PNFDemo4'] - '2 properties, no matches' | [Contact: 'newemailforstore@bookstore.com', Contact2: ''] || [] - } - - def 'Query cm handles on trust level'() { - given: 'query properties for trust level COMPLETE' - def trustLevelPropertyQueryPairs = ['trustLevel' : TrustLevel.COMPLETE.toString()] - and: 'the dmi cache has been initialised and "knows" about my-dmi-plugin-identifier' - trustLevelPerDmiPlugin.put('my-dmi-plugin-identifier', TrustLevel.COMPLETE) - and: 'the DataNodes queried for a given cpsPath are returned from the persistence service' - mockResponses() - when: 'the query is run' - def result = objectUnderTest.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs) - then: 'the result contain trusted PNFDemo' - assert result.size() == 1 - assert result[0] == 'PNFDemo' - } - - def 'Query CmHandles using empty public properties query pair.'() { - when: 'a query on CmHandle public properties is executed using an empty map' - def result = objectUnderTest.queryCmHandlePublicProperties([:]) - then: 'no cm handles are returned' - result.size() == 0 - } - - def 'Query CmHandles using empty private properties query pair.'() { - when: 'a query on CmHandle private properties is executed using an empty map' - def result = objectUnderTest.queryCmHandleAdditionalProperties([:]) - then: 'no cm handles are returned' - result.size() == 0 - } - - def 'Query CmHandles by a private field\'s value.'() { - given: 'a data node exists with a certain additional-property' - mockCpsDataPersistenceService.queryDataNodes(_, _, dataNodeWithPrivateField, _) >> [pnfDemo5] - when: 'a query on CmHandle private properties is executed using a map' - def result = objectUnderTest.queryCmHandleAdditionalProperties(['Contact3': 'newemailforstore3@bookstore.com']) - then: 'one cm handle is returned' - result.size() == 1 - } - - def 'Get CmHandles by it\'s state.'() { - given: 'a cm handle state to query' - def cmHandleState = CmHandleState.ADVISED - and: 'the persistence service returns a list of data nodes' - mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS) >> sampleDataNodes - when: 'cm handles are fetched by state' - def result = objectUnderTest.queryCmHandlesByState(cmHandleState) - then: 'the returned result matches the result from the persistence service' - assert result == sampleDataNodes - } - - def 'Check the state of a cmHandle when #scenario.'() { - given: 'a cm handle state to compare' - def cmHandleState = state - and: 'the persistence service returns a list of data nodes' - mockCpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - NCMP_DMI_REGISTRY_PARENT + '/cm-handles[@id=\'some-cm-handle\']/state', - OMIT_DESCENDANTS) >> [new DataNode(leaves: ['cm-handle-state': 'READY'])] - when: 'cm handles are compared by state' - def result = objectUnderTest.cmHandleHasState('some-cm-handle', cmHandleState) - then: 'the returned result matches the expected result from the persistence service' - result == expectedResult - where: - scenario | state || expectedResult - 'the provided state matches' | CmHandleState.READY || true - 'the provided state does not match'| CmHandleState.DELETED || false - } - - def 'Get Cm Handles state by Cm-Handle Id'() { - given: 'a cm handle state to query' - def cmHandleState = CmHandleState.READY - and: 'cps data service returns a list of data nodes' - mockCpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - NCMP_DMI_REGISTRY_PARENT + '/cm-handles[@id=\'some-cm-handle\']/state', - OMIT_DESCENDANTS) >> [new DataNode(leaves: ['cm-handle-state': 'READY'])] - when: 'cm handles are fetched by state and id' - def result = objectUnderTest.getCmHandleState('some-cm-handle') - then: 'the returned result is a list of data nodes returned by cps data service' - assert result == new DataNode(leaves: ['cm-handle-state': 'READY']) - } - - def 'Retrieve Cm Handles By Operational Sync State : UNSYNCHRONIZED'() { - given: 'a cm handle state to query' - def cmHandleState = CmHandleState.READY - and: 'cps data service returns a list of data nodes' - mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes - when: 'cm handles are fetched by the UNSYNCHRONIZED operational sync state' - def result = objectUnderTest.queryCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED) - then: 'the returned result is a list of data nodes returned by cps data service' - assert result == sampleDataNodes - } - - def 'Retrieve cm handle by cps path '() { - given: 'a cm handle state to query based on the cps path' - def cmHandleDataNode = new DataNode(xpath: 'xpath', leaves: ['cm-handle-state': 'LOCKED']) - def cpsPath = '//cps-path' - and: 'cps data service returns a valid data node' - mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - cpsPath + '/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS) - >> Arrays.asList(cmHandleDataNode) - when: 'get cm handles by cps path is invoked' - def result = objectUnderTest.queryCmHandleAncestorsByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS) - then: 'the returned result is a list of data nodes returned by cps data service' - assert result.contains(cmHandleDataNode) - } - - def 'Get all cm handles by dmi plugin identifier'() { - given: 'the DataNodes queried for a given cpsPath are returned from the persistence service.' - mockResponses() - when: 'cm Handles are fetched for a given dmi plugin identifier' - def result = objectUnderTest.getCmHandleIdsByDmiPluginIdentifier('my-dmi-plugin-identifier') - then: 'result is the correct size' - assert result.size() == 3 - and: 'result contains the correct cm handles' - assert result.containsAll('PNFDemo', 'PNFDemo2', 'PNFDemo4') - } - - void mockResponses() { - mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact\" and @value=\"newemailforstore@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo2, pnfDemo4] - mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"wont_match\" and @value=\"wont_match\"]/ancestor::cm-handles', _) >> [] - mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"newemailforstore2@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo4] - mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"\"]/ancestor::cm-handles', _) >> [] - mockCpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"READY\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo3] - mockCpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"LOCKED\"]/ancestor::cm-handles', _) >> [pnfDemo2, pnfDemo4] - mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo, pnfDemo2] - mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-data-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo, pnfDemo4] - mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-model-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo2, pnfDemo4] - } - - def static createDataNode(dataNodeId) { - return new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'' + dataNodeId + '\']', leaves: ['id':dataNodeId]) - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryServiceImplSpec.groovy new file mode 100644 index 0000000000..bdf431855f --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueryServiceImplSpec.groovy @@ -0,0 +1,206 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2023 Nordix Foundation + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.inventory + +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel +import org.onap.cps.spi.utils.CpsValidator +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT +import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS +import org.onap.cps.spi.CpsDataPersistenceService +import org.onap.cps.spi.model.DataNode +import spock.lang.Shared +import spock.lang.Specification + +class CmHandleQueryServiceImplSpec extends Specification { + + def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService) + + def trustLevelPerDmiPlugin = [:] + + def trustLevelPerCmHandle = [ 'PNFDemo': TrustLevel.COMPLETE, 'PNFDemo2': TrustLevel.NONE, 'PNFDemo4': TrustLevel.NONE ] + + def mockCpsValidator = Mock(CpsValidator) + + def objectUnderTest = new CmHandleQueryServiceImpl(mockCpsDataPersistenceService, trustLevelPerDmiPlugin, trustLevelPerCmHandle, mockCpsValidator) + + @Shared + def static sampleDataNodes = [new DataNode()] + + def dataNodeWithPrivateField = '//additional-properties[@name=\"Contact3\" and @value=\"newemailforstore3@bookstore.com\"]/ancestor::cm-handles' + + def static pnfDemo = createDataNode('PNFDemo') + def static pnfDemo2 = createDataNode('PNFDemo2') + def static pnfDemo3 = createDataNode('PNFDemo3') + def static pnfDemo4 = createDataNode('PNFDemo4') + def static pnfDemo5 = createDataNode('PNFDemo5') + + def 'Query CmHandles with public properties query pair.'() { + given: 'the DataNodes queried for a given cpsPath are returned from the persistence service.' + mockResponses() + when: 'a query on cmhandle public properties is performed with a public property pair' + def result = objectUnderTest.queryCmHandlePublicProperties(publicPropertyPairs) + then: 'the correct cm handle data objects are returned' + result.containsAll(expectedCmHandleIds) + result.size() == expectedCmHandleIds.size() + where: 'the following data is used' + scenario | publicPropertyPairs || expectedCmHandleIds + 'single property matches' | [Contact: 'newemailforstore@bookstore.com'] || ['PNFDemo', 'PNFDemo2', 'PNFDemo4'] + 'public property does not match' | [wont_match: 'wont_match'] || [] + '2 properties, only one match' | [Contact: 'newemailforstore@bookstore.com', Contact2: 'newemailforstore2@bookstore.com'] || ['PNFDemo4'] + '2 properties, no matches' | [Contact: 'newemailforstore@bookstore.com', Contact2: ''] || [] + } + + def 'Query cm handles on trust level'() { + given: 'query properties for trust level COMPLETE' + def trustLevelPropertyQueryPairs = ['trustLevel' : TrustLevel.COMPLETE.toString()] + and: 'the dmi cache has been initialised and "knows" about my-dmi-plugin-identifier' + trustLevelPerDmiPlugin.put('my-dmi-plugin-identifier', TrustLevel.COMPLETE) + and: 'the DataNodes queried for a given cpsPath are returned from the persistence service' + mockResponses() + when: 'the query is run' + def result = objectUnderTest.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs) + then: 'the result contain trusted PNFDemo' + assert result.size() == 1 + assert result[0] == 'PNFDemo' + } + + def 'Query CmHandles using empty public properties query pair.'() { + when: 'a query on CmHandle public properties is executed using an empty map' + def result = objectUnderTest.queryCmHandlePublicProperties([:]) + then: 'no cm handles are returned' + result.size() == 0 + } + + def 'Query CmHandles using empty private properties query pair.'() { + when: 'a query on CmHandle private properties is executed using an empty map' + def result = objectUnderTest.queryCmHandleAdditionalProperties([:]) + then: 'no cm handles are returned' + result.size() == 0 + } + + def 'Query CmHandles by a private field\'s value.'() { + given: 'a data node exists with a certain additional-property' + mockCpsDataPersistenceService.queryDataNodes(_, _, dataNodeWithPrivateField, _) >> [pnfDemo5] + when: 'a query on CmHandle private properties is executed using a map' + def result = objectUnderTest.queryCmHandleAdditionalProperties(['Contact3': 'newemailforstore3@bookstore.com']) + then: 'one cm handle is returned' + result.size() == 1 + } + + def 'Get CmHandles by it\'s state.'() { + given: 'a cm handle state to query' + def cmHandleState = CmHandleState.ADVISED + and: 'the persistence service returns a list of data nodes' + mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS) >> sampleDataNodes + when: 'cm handles are fetched by state' + def result = objectUnderTest.queryCmHandlesByState(cmHandleState) + then: 'the returned result matches the result from the persistence service' + assert result == sampleDataNodes + } + + def 'Check the state of a cmHandle when #scenario.'() { + given: 'a cm handle state to compare' + def cmHandleState = state + and: 'the persistence service returns a list of data nodes' + mockCpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + NCMP_DMI_REGISTRY_PARENT + '/cm-handles[@id=\'some-cm-handle\']/state', + OMIT_DESCENDANTS) >> [new DataNode(leaves: ['cm-handle-state': 'READY'])] + when: 'cm handles are compared by state' + def result = objectUnderTest.cmHandleHasState('some-cm-handle', cmHandleState) + then: 'the returned result matches the expected result from the persistence service' + result == expectedResult + where: + scenario | state || expectedResult + 'the provided state matches' | CmHandleState.READY || true + 'the provided state does not match'| CmHandleState.DELETED || false + } + + def 'Get Cm Handles state by Cm-Handle Id'() { + given: 'a cm handle state to query' + def cmHandleState = CmHandleState.READY + and: 'cps data service returns a list of data nodes' + mockCpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + NCMP_DMI_REGISTRY_PARENT + '/cm-handles[@id=\'some-cm-handle\']/state', + OMIT_DESCENDANTS) >> [new DataNode(leaves: ['cm-handle-state': 'READY'])] + when: 'cm handles are fetched by state and id' + def result = objectUnderTest.getCmHandleState('some-cm-handle') + then: 'the returned result is a list of data nodes returned by cps data service' + assert result == new DataNode(leaves: ['cm-handle-state': 'READY']) + } + + def 'Retrieve Cm Handles By Operational Sync State : UNSYNCHRONIZED'() { + given: 'a cm handle state to query' + def cmHandleState = CmHandleState.READY + and: 'cps data service returns a list of data nodes' + mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes + when: 'cm handles are fetched by the UNSYNCHRONIZED operational sync state' + def result = objectUnderTest.queryCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED) + then: 'the returned result is a list of data nodes returned by cps data service' + assert result == sampleDataNodes + } + + def 'Retrieve cm handle by cps path '() { + given: 'a cm handle state to query based on the cps path' + def cmHandleDataNode = new DataNode(xpath: 'xpath', leaves: ['cm-handle-state': 'LOCKED']) + def cpsPath = '//cps-path' + and: 'cps data service returns a valid data node' + mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + cpsPath + '/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS) + >> Arrays.asList(cmHandleDataNode) + when: 'get cm handles by cps path is invoked' + def result = objectUnderTest.queryCmHandleAncestorsByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS) + then: 'the returned result is a list of data nodes returned by cps data service' + assert result.contains(cmHandleDataNode) + } + + def 'Get all cm handles by dmi plugin identifier'() { + given: 'the DataNodes queried for a given cpsPath are returned from the persistence service.' + mockResponses() + when: 'cm Handles are fetched for a given dmi plugin identifier' + def result = objectUnderTest.getCmHandleIdsByDmiPluginIdentifier('my-dmi-plugin-identifier') + then: 'result is the correct size' + assert result.size() == 3 + and: 'result contains the correct cm handles' + assert result.containsAll('PNFDemo', 'PNFDemo2', 'PNFDemo4') + } + + void mockResponses() { + mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact\" and @value=\"newemailforstore@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo2, pnfDemo4] + mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"wont_match\" and @value=\"wont_match\"]/ancestor::cm-handles', _) >> [] + mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"newemailforstore2@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo4] + mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"\"]/ancestor::cm-handles', _) >> [] + mockCpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"READY\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo3] + mockCpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"LOCKED\"]/ancestor::cm-handles', _) >> [pnfDemo2, pnfDemo4] + mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo, pnfDemo2] + mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-data-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo, pnfDemo4] + mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-model-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo2, pnfDemo4] + } + + def static createDataNode(dataNodeId) { + return new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'' + dataNodeId + '\']', leaves: ['id':dataNodeId]) + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy index 66fd7d88ef..775824bb07 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy @@ -61,7 +61,7 @@ class InventoryPersistenceImplSpec extends Specification { def mockCpsValidator = Mock(CpsValidator) - def mockCmHandleQueries = Mock(CmHandleQueries) + def mockCmHandleQueries = Mock(CmHandleQueryService) def objectUnderTest = new InventoryPersistenceImpl(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService, mockCpsValidator, mockCpsAnchorService, mockCmHandleQueries) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy index 44bc182000..e9038ecfc9 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy @@ -35,7 +35,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService import org.onap.cps.ncmp.api.impl.inventory.CmHandleState import org.onap.cps.ncmp.api.impl.inventory.CompositeState import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder @@ -52,7 +52,7 @@ import java.util.stream.Collectors class ModuleOperationsUtilsSpec extends Specification{ - def mockCmHandleQueries = Mock(CmHandleQueries) + def mockCmHandleQueries = Mock(CmHandleQueryService) def mockDmiDataOperations = Mock(DmiDataOperations) @@ -202,7 +202,7 @@ class ModuleOperationsUtilsSpec extends Specification{ def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}' JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString); def responseEntity = new ResponseEntity<>(jsonNode, HttpStatus.OK) - mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.datastoreName, 'cm-handle-123', _) >> responseEntity + mockDmiDataOperations.getAllResourceDataFromDmi('cm-handle-123', _) >> responseEntity when: 'get resource data is called' def result = objectUnderTest.getResourceData('cm-handle-123') then: 'the returned data is correct' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncServiceSpec.groovy index cb933fafbe..9f7b953003 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncServiceSpec.groovy @@ -32,7 +32,7 @@ import org.onap.cps.api.CpsModuleService import org.onap.cps.spi.model.DataNodeBuilder import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.spi.CascadeDeleteAllowed @@ -46,7 +46,7 @@ class ModuleSyncServiceSpec extends Specification { def mockCpsModuleService = Mock(CpsModuleService) def mockDmiModelOperations = Mock(DmiModelOperations) def mockCpsAnchorService = Mock(CpsAnchorService) - def mockCmHandleQueries = Mock(CmHandleQueries) + def mockCmHandleQueries = Mock(CmHandleQueryService) def mockCpsDataService = Mock(CpsDataService) def mockJsonObjectMapper = Mock(JsonObjectMapper) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy index b286e9fb10..a0c6d0027e 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy @@ -78,7 +78,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { def responseFromDmi = Mono.just(new ResponseEntity('{some-key:some-value}', HttpStatus.OK)) def expectedUrl = "${dmiServiceBaseUrl}${expectedDatastoreInUrl}?resourceIdentifier=${resourceIdentifier}${expectedOptionsInUrl}" def expectedJson = '{"operation":"read","cmHandleProperties":' + expectedProperties + ',"moduleSetTag":""}' - mockDmiRestClient.postOperationWithJsonDataAsync(DATA, expectedUrl, expectedJson, READ, NO_AUTH_HEADER) >> responseFromDmi + mockDmiRestClient.asynchronousPostOperationWithJsonData(DATA, expectedUrl, expectedJson, READ, NO_AUTH_HEADER) >> responseFromDmi when: 'get resource data is invoked' def cmResourceAddress = new CmResourceAddress(dataStore.datastoreName, cmHandleId, resourceIdentifier) def result = objectUnderTest.getResourceDataFromDmi(cmResourceAddress, options, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER).block() @@ -105,11 +105,11 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { def responseFromDmi = Mono.just(new ResponseEntity(HttpStatus.ACCEPTED)) def expectedDmiBatchResourceDataUrl = "someServiceName/dmi/v1/data?requestId=requestId&topic=my-topic-name" def expectedBatchRequestAsJson = '{"operations":[{"operation":"read","operationId":"operational-14","datastore":"ncmp-datastore:passthrough-operational","options":"some option","resourceIdentifier":"some resource identifier","cmHandles":[{"id":"some-cm-handle","moduleSetTag":"","cmHandleProperties":{"prop1":"val1"}}]}]}' - mockDmiRestClient.postOperationWithJsonDataAsync(DATA, expectedDmiBatchResourceDataUrl, _, READ, NO_AUTH_HEADER) >> responseFromDmi + mockDmiRestClient.asynchronousPostOperationWithJsonData(DATA, expectedDmiBatchResourceDataUrl, _, READ, NO_AUTH_HEADER) >> responseFromDmi when: 'get resource data for group of cm handles is invoked' objectUnderTest.requestResourceDataFromDmi('my-topic-name', dataOperationRequest, 'requestId', NO_AUTH_HEADER) then: 'the post operation was called with the expected URL and JSON request body' - 1 * mockDmiRestClient.postOperationWithJsonDataAsync(DATA, expectedDmiBatchResourceDataUrl, expectedBatchRequestAsJson, READ, NO_AUTH_HEADER) + 1 * mockDmiRestClient.asynchronousPostOperationWithJsonData(DATA, expectedDmiBatchResourceDataUrl, expectedBatchRequestAsJson, READ, NO_AUTH_HEADER) } def 'Execute (async) data operation from DMI service with Exception.'() { @@ -122,7 +122,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { def actualDataOperationCloudEvent = null eventsPublisher.publishCloudEvent('my-topic-name', 'my-request-id', _) >> { args -> actualDataOperationCloudEvent = args[2] } and: 'a DMI client request exception is thrown when DMI service is called' - mockDmiRestClient.postOperationWithJsonDataAsync(*_) >> { Mono.error(new DmiClientRequestException(123, '', '', UNKNOWN_ERROR)) } + mockDmiRestClient.asynchronousPostOperationWithJsonData(*_) >> { Mono.error(new DmiClientRequestException(123, '', '', UNKNOWN_ERROR)) } when: 'attempt to get resource data for group of cm handles is invoked' objectUnderTest.requestResourceDataFromDmi('my-topic-name', dataOperationRequest, 'my-request-id', NO_AUTH_HEADER) then: 'the event contains the expected error details' @@ -141,9 +141,9 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { def responseFromDmi = new ResponseEntity(HttpStatus.OK) def expectedUrl = dmiServiceBaseUrl + "passthrough-operational?resourceIdentifier=/" def expectedJson = '{"operation":"read","cmHandleProperties":{"prop1":"val1"},"moduleSetTag":"my-module-set-tag"}' - mockDmiRestClient.postOperationWithJsonData(DATA, expectedUrl, expectedJson, READ, null) >> responseFromDmi + mockDmiRestClient.synchronousPostOperationWithJsonData(DATA, expectedUrl, expectedJson, READ, null) >> responseFromDmi when: 'get resource data is invoked' - def result = objectUnderTest.getResourceDataFromDmi( PASSTHROUGH_OPERATIONAL.datastoreName, cmHandleId, NO_REQUEST_ID) + def result = objectUnderTest.getAllResourceDataFromDmi(cmHandleId, NO_REQUEST_ID) then: 'the result is the response from the DMI service' assert result == responseFromDmi } @@ -155,7 +155,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { def expectedUrl = "${dmiServiceBaseUrl}passthrough-running?resourceIdentifier=${resourceIdentifier}" def expectedJson = '{"operation":"' + expectedOperationInUrl + '","dataType":"some data type","data":"requestData","cmHandleProperties":{"prop1":"val1"},"moduleSetTag":""}' def responseFromDmi = new ResponseEntity(HttpStatus.OK) - mockDmiRestClient.postOperationWithJsonData(DATA, expectedUrl, expectedJson, operation, NO_AUTH_HEADER) >> responseFromDmi + mockDmiRestClient.synchronousPostOperationWithJsonData(DATA, expectedUrl, expectedJson, operation, NO_AUTH_HEADER) >> responseFromDmi when: 'write resource method is invoked' def result = objectUnderTest.writeResourceDataPassThroughRunningFromDmi(cmHandleId, 'parent/child', operation, 'requestData', 'some data type', NO_AUTH_HEADER) then: 'the result is the response from the DMI service' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy index db7f26f5f1..9ab52b9467 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy @@ -59,7 +59,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { def moduleReferencesAsLisOfMaps = [[moduleName: 'mod1', revision: 'A'], [moduleName: 'mod2', revision: 'X']] def expectedUrl = "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules" def responseFromDmi = new ResponseEntity([schemas: moduleReferencesAsLisOfMaps], HttpStatus.OK) - mockDmiRestClient.postOperationWithJsonData(MODEL, expectedUrl, '{"cmHandleProperties":{},"moduleSetTag":""}', READ, NO_AUTH_HEADER) >> responseFromDmi + mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, expectedUrl, '{"cmHandleProperties":{},"moduleSetTag":""}', READ, NO_AUTH_HEADER) >> responseFromDmi when: 'get module references is called' def result = objectUnderTest.getModuleReferences(yangModelCmHandle) then: 'the result consists of expected module references' @@ -72,7 +72,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { and: 'any response from DMI service when it is called with the expected parameters' // TODO (toine): production code ignores any error code from DMI, this should be improved in future def responseFromDmi = new ResponseEntity(bodyAsMap, HttpStatus.NO_CONTENT) - mockDmiRestClient.postOperationWithJsonData(*_) >> responseFromDmi + mockDmiRestClient.synchronousPostOperationWithJsonData(*_) >> responseFromDmi when: 'get module references is called' def result = objectUnderTest.getModuleReferences(yangModelCmHandle) then: 'the result is empty' @@ -90,7 +90,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { mockYangModelCmHandleRetrieval(dmiProperties) and: 'a positive response from DMI service when it is called with tha expected parameters' def responseFromDmi = new ResponseEntity(HttpStatus.OK) - mockDmiRestClient.postOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules", + mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules", '{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + ',"moduleSetTag":""}', READ, NO_AUTH_HEADER) >> responseFromDmi when: 'a get module references is called' def result = objectUnderTest.getModuleReferences(yangModelCmHandle) @@ -109,7 +109,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { def responseFromDmi = new ResponseEntity([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source'], [moduleName: 'mod2', revision: 'C', yangSource: 'other yang source']], HttpStatus.OK) def expectedModuleReferencesInRequest = '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' - mockDmiRestClient.postOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", + mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', READ, NO_AUTH_HEADER) >> responseFromDmi when: 'get new yang resources from DMI service' def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, newModuleReferences) @@ -125,7 +125,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { and: 'a positive response from DMI service when it is called with tha expected parameters' // TODO (toine): production code ignores any error code from DMI, this should be improved in future def responseFromDmi = new ResponseEntity(responseFromDmiBody, HttpStatus.NO_CONTENT) - mockDmiRestClient.postOperationWithJsonData(*_) >> responseFromDmi + mockDmiRestClient.synchronousPostOperationWithJsonData(*_) >> responseFromDmi when: 'get new yang resources from DMI service' def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, newModuleReferences) then: 'the result is empty' @@ -141,7 +141,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { mockYangModelCmHandleRetrieval(dmiProperties) and: 'a positive response from DMI service when it is called with the expected moduleSetTag, modules and properties' def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK) - mockDmiRestClient.postOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", + mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", '{"data":{"modules":[{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}]},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ, NO_AUTH_HEADER) >> responseFromDmi when: 'get new yang resources from DMI service' @@ -159,7 +159,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { mockYangModelCmHandleRetrieval([], moduleSetTag) and: 'a positive response from DMI service when it is called with the expected moduleSetTag' def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK) - mockDmiRestClient.postOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", + mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", '{' + expectedModuleSetTagInRequest + '"data":{"modules":[{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}]},"cmHandleProperties":{}}', READ, NO_AUTH_HEADER) >> responseFromDmi when: 'get new yang resources from DMI service' @@ -180,7 +180,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { then: 'no resources are returned' assert result == [:] and: 'no request is sent to DMI' - 0 * mockDmiRestClient.postOperationWithJsonData(*_) + 0 * mockDmiRestClient.synchronousPostOperationWithJsonData(*_) } def 'Retrieving yang resources from DMI with null DMI properties.'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDogSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDogSpec.groovy index 446126be94..53057573f3 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDogSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDogSpec.groovy @@ -20,8 +20,8 @@ package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability -import org.onap.cps.ncmp.api.NetworkCmProxyDataService import org.onap.cps.ncmp.api.impl.client.DmiRestClient +import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueryService import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager import spock.lang.Specification @@ -29,15 +29,12 @@ import spock.lang.Specification class DmiPluginWatchDogSpec extends Specification { def mockDmiRestClient = Mock(DmiRestClient) - def mockNetworkCmProxyDataService = Mock(NetworkCmProxyDataService) + def mockCmHandleQueryService = Mock(CmHandleQueryService) def mockTrustLevelManager = Mock(TrustLevelManager) def trustLevelPerDmiPlugin = [:] - def objectUnderTest = new DmiPluginWatchDog(mockDmiRestClient, - mockNetworkCmProxyDataService, - mockTrustLevelManager, - trustLevelPerDmiPlugin) + def objectUnderTest = new DmiPluginWatchDog(mockDmiRestClient, mockCmHandleQueryService, mockTrustLevelManager, trustLevelPerDmiPlugin) def 'watch dmi plugin health status for #dmiHealhStatus'() { given: 'the cache has been initialised and "knows" about dmi-1' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy index 85f479f2a0..0c5c8165f1 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy @@ -21,14 +21,7 @@ package org.onap.cps.integration.base -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT - -import java.time.OffsetDateTime -import java.time.format.DateTimeFormatter import okhttp3.mockwebserver.MockWebServer -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsDataspaceService @@ -36,20 +29,22 @@ import org.onap.cps.api.CpsModuleService import org.onap.cps.api.CpsQueryService import org.onap.cps.integration.DatabaseTestContainer import org.onap.cps.integration.KafkaTestContainer -import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService -import org.onap.cps.ncmp.api.NetworkCmProxyDataService import org.onap.cps.ncmp.api.NetworkCmProxyQueryService +import org.onap.cps.ncmp.api.ParameterizedCmHandleQueryService +import org.onap.cps.ncmp.api.impl.NetworkCmProxyFacade +import org.onap.cps.ncmp.api.impl.NetworkCmProxyInventoryFacade import org.onap.cps.ncmp.api.impl.inventory.CmHandleState +import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncWatchdog import org.onap.cps.ncmp.api.models.DmiPluginRegistration import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle +import org.onap.cps.ncmp.utils.AlternateIdMatcher import org.onap.cps.spi.exceptions.DataspaceNotFoundException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.repository.DataspaceRepository import org.onap.cps.spi.utils.SessionManager -import org.onap.cps.ncmp.utils.AlternateIdMatcher -import org.onap.cps.utils.JsonObjectMapper import org.onap.cps.utils.ContentType +import org.onap.cps.utils.JsonObjectMapper import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.autoconfigure.domain.EntityScan @@ -63,6 +58,13 @@ import spock.lang.Shared import spock.lang.Specification import spock.util.concurrent.PollingConditions +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter + +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT + @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = [CpsDataspaceService]) @Testcontainers @EnableAutoConfiguration @@ -100,10 +102,13 @@ abstract class CpsIntegrationSpecBase extends Specification { SessionManager sessionManager @Autowired - NetworkCmProxyCmHandleQueryService networkCmProxyCmHandleQueryService + ParameterizedCmHandleQueryService networkCmProxyCmHandleQueryService + + @Autowired + NetworkCmProxyFacade networkCmProxyFacade @Autowired - NetworkCmProxyDataService networkCmProxyDataService + NetworkCmProxyInventoryFacade NetworkCmProxyInventoryFacade @Autowired NetworkCmProxyQueryService networkCmProxyQueryService @@ -212,10 +217,10 @@ abstract class CpsIntegrationSpecBase extends Specification { def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag) { def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, moduleSetTag: moduleSetTag) - networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: [cmHandleToCreate])) + networkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: [cmHandleToCreate])) moduleSyncWatchdog.moduleSyncAdvisedCmHandles() new PollingConditions().within(3, () -> { - CmHandleState.READY == networkCmProxyDataService.getCmHandleCompositeState(cmHandleId).cmHandleState + CmHandleState.READY == networkCmProxyInventoryFacade.getCmHandleCompositeState(cmHandleId).cmHandleState }) } @@ -224,7 +229,7 @@ abstract class CpsIntegrationSpecBase extends Specification { } def deregisterCmHandles(dmiPlugin, cmHandleIds) { - networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds)) + networkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds)) } def overrideCmHandleLastUpdateTime(cmHandleId, newUpdateTime) { diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpBearerTokenPassthroughSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpBearerTokenPassthroughSpec.groovy index 4ffe586a99..2a35313f74 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpBearerTokenPassthroughSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpBearerTokenPassthroughSpec.groovy @@ -20,14 +20,6 @@ package org.onap.cps.integration.functional -import static org.springframework.http.HttpMethod.GET -import static org.springframework.http.HttpMethod.DELETE -import static org.springframework.http.HttpMethod.PATCH -import static org.springframework.http.HttpMethod.POST -import static org.springframework.http.HttpMethod.PUT -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status - import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest @@ -38,6 +30,14 @@ import org.springframework.http.HttpStatus import org.springframework.http.MediaType import spock.util.concurrent.PollingConditions +import static org.springframework.http.HttpMethod.DELETE +import static org.springframework.http.HttpMethod.GET +import static org.springframework.http.HttpMethod.PATCH +import static org.springframework.http.HttpMethod.POST +import static org.springframework.http.HttpMethod.PUT +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase { def lastAuthHeaderReceived = null diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy index 5c337f179b..8186c41914 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy @@ -20,13 +20,11 @@ package org.onap.cps.integration.functional -import java.time.Duration -import java.time.OffsetDateTime import org.apache.kafka.common.TopicPartition import org.apache.kafka.common.serialization.StringDeserializer import org.onap.cps.integration.KafkaTestContainer import org.onap.cps.integration.base.CpsIntegrationSpecBase -import org.onap.cps.ncmp.api.NetworkCmProxyDataService +import org.onap.cps.ncmp.api.impl.NetworkCmProxyInventoryFacade import org.onap.cps.ncmp.api.impl.inventory.CmHandleState import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse @@ -35,14 +33,17 @@ import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.ncmp.events.lcm.v1.LcmEvent import spock.util.concurrent.PollingConditions +import java.time.Duration +import java.time.OffsetDateTime + class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { - NetworkCmProxyDataService objectUnderTest + NetworkCmProxyInventoryFacade objectUnderTest def kafkaConsumer = KafkaTestContainer.getConsumer('ncmp-group', StringDeserializer.class) def setup() { - objectUnderTest = networkCmProxyDataService + objectUnderTest = networkCmProxyInventoryFacade } def 'CM Handle registration is successful.'() { @@ -55,7 +56,7 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { when: 'a CM-handle is registered for creation' def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1') def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]) - def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'registration gives successful response' assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse('ch-1')] @@ -93,7 +94,7 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { when: 'a CM-handle is registered for creation' def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1') def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]) - networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) and: 'module sync runs' moduleSyncWatchdog.moduleSyncAdvisedCmHandles() @@ -121,7 +122,7 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { when: 'a CM-handle is registered for creation with moduleSetTag "B"' def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B') - networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])) + objectUnderTest.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])) then: 'the CM-handle goes to READY state after module sync' moduleSyncWatchdog.moduleSyncAdvisedCmHandles() @@ -146,7 +147,7 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { when: 'CM-handles are registered for creation' def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1'), new NcmpServiceCmHandle(cmHandleId: 'ch-2')] def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: cmHandlesToCreate) - networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) and: 'module sync runs' moduleSyncWatchdog.moduleSyncAdvisedCmHandles() then: 'CM-handles go to LOCKED state' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy index 4d1d77e694..f5c0b7bea7 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy @@ -21,7 +21,7 @@ package org.onap.cps.integration.functional import org.onap.cps.integration.base.CpsIntegrationSpecBase -import org.onap.cps.ncmp.api.NetworkCmProxyDataService +import org.onap.cps.ncmp.api.impl.NetworkCmProxyInventoryFacade import org.onap.cps.ncmp.api.impl.inventory.CmHandleState import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse @@ -31,13 +31,13 @@ import spock.util.concurrent.PollingConditions class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { - NetworkCmProxyDataService objectUnderTest + NetworkCmProxyInventoryFacade objectUnderTest static final CM_HANDLE_ID = 'ch-1' static final CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG = 'ch-2' def setup() { - objectUnderTest = networkCmProxyDataService + objectUnderTest = networkCmProxyInventoryFacade } def 'Upgrade CM-handle with new moduleSetTag or no moduleSetTag.'() { @@ -48,7 +48,7 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { when: "the CM-handle is upgraded with given moduleSetTag '${updatedModuleSetTag}'" def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: updatedModuleSetTag) - def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule( new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) then: 'registration gives successful response' @@ -101,7 +101,7 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { when: "CM-handle is upgraded to moduleSetTag '${updatedModuleSetTag}'" def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: updatedModuleSetTag) - def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule( new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) then: 'registration gives successful response' @@ -139,7 +139,7 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { when: 'CM-handle is upgraded with the same moduleSetTag' def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'same') - networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + objectUnderTest.updateDmiRegistrationAndSyncModule( new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) then: 'CM-handle remains in READY state' @@ -164,7 +164,7 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { when: 'the CM-handle is upgraded' def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'newTag') - networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + objectUnderTest.updateDmiRegistrationAndSyncModule( new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) and: 'module sync runs' -- cgit 1.2.3-korg