diff options
48 files changed, 484 insertions, 321 deletions
diff --git a/cps-application/src/main/java/org/onap/cps/config/MicroMeterConfig.java b/cps-application/src/main/java/org/onap/cps/config/MicroMeterConfig.java index 6782669db2..6bf3f87d17 100644 --- a/cps-application/src/main/java/org/onap/cps/config/MicroMeterConfig.java +++ b/cps-application/src/main/java/org/onap/cps/config/MicroMeterConfig.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023-2025 Nordix Foundation. + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,11 +20,9 @@ package org.onap.cps.config; -import com.hazelcast.map.IMap; import io.github.mweirauch.micrometer.jvm.extras.ProcessMemoryMetrics; import io.github.mweirauch.micrometer.jvm.extras.ProcessThreadMetrics; import io.micrometer.core.aop.TimedAspect; -import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; import lombok.RequiredArgsConstructor; @@ -36,10 +34,6 @@ import org.springframework.context.annotation.Configuration; @RequiredArgsConstructor public class MicroMeterConfig { - private static final String STATE_TAG = "state"; - private static final String CM_HANDLE_STATE_GAUGE = "cps_ncmp_inventory_cm_handles_by_state"; - final IMap<String, Integer> cmHandlesByState; - @Bean public TimedAspect timedAspect(final MeterRegistry meterRegistry) { return new TimedAspect(meterRegistry); @@ -57,79 +51,4 @@ public class MicroMeterConfig { return new ProcessThreadMetrics(); } - /** - * Register gauge metric for cm handles with state 'advised'. - * - * @param meterRegistry meter registry - * @return cm handle state gauge - */ - @Bean - public Gauge advisedCmHandles(final MeterRegistry meterRegistry) { - return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, - value -> cmHandlesByState.get("advisedCmHandlesCount")) - .tag(STATE_TAG, "ADVISED") - .description("Current number of cm handles in advised state") - .register(meterRegistry); - } - - /** - * Register gauge metric for cm handles with state 'ready'. - * - * @param meterRegistry meter registry - * @return cm handle state gauge - */ - @Bean - public Gauge readyCmHandles(final MeterRegistry meterRegistry) { - return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, - value -> cmHandlesByState.get("readyCmHandlesCount")) - .tag(STATE_TAG, "READY") - .description("Current number of cm handles in ready state") - .register(meterRegistry); - } - - /** - * Register gauge metric for cm handles with state 'locked'. - * - * @param meterRegistry meter registry - * @return cm handle state gauge - */ - @Bean - public Gauge lockedCmHandles(final MeterRegistry meterRegistry) { - return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, - value -> cmHandlesByState.get("lockedCmHandlesCount")) - .tag(STATE_TAG, "LOCKED") - .description("Current number of cm handles in locked state") - .register(meterRegistry); - } - - /** - * Register gauge metric for cm handles with state 'deleting'. - * - * @param meterRegistry meter registry - * @return cm handle state gauge - */ - @Bean - public Gauge deletingCmHandles(final MeterRegistry meterRegistry) { - return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, - value -> cmHandlesByState.get("deletingCmHandlesCount")) - .tag(STATE_TAG, "DELETING") - .description("Current number of cm handles in deleting state") - .register(meterRegistry); - } - - /** - * Register gauge metric for cm handles with state 'deleted'. - * - * @param meterRegistry meter registry - * @return cm handle state gauge - */ - @Bean - public Gauge deletedCmHandles(final MeterRegistry meterRegistry) { - return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, - value -> cmHandlesByState.get("deletedCmHandlesCount")) - .tag(STATE_TAG, "DELETED") - .description("Number of cm handles that have been deleted since the application started") - .register(meterRegistry); - } - } diff --git a/cps-application/src/test/groovy/org/onap/cps/config/MicroMeterConfigSpec.groovy b/cps-application/src/test/groovy/org/onap/cps/config/MicroMeterConfigSpec.groovy index faef32b04b..29cb65cfbb 100644 --- a/cps-application/src/test/groovy/org/onap/cps/config/MicroMeterConfigSpec.groovy +++ b/cps-application/src/test/groovy/org/onap/cps/config/MicroMeterConfigSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023-2025 Nordix Foundation. + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,14 +20,12 @@ package org.onap.cps.config -import com.hazelcast.map.IMap import io.micrometer.core.instrument.simple.SimpleMeterRegistry import spock.lang.Specification class MicroMeterConfigSpec extends Specification { - def cmHandlesByState = Mock(IMap) - def objectUnderTest = new MicroMeterConfig(cmHandlesByState) + def objectUnderTest = new MicroMeterConfig() def simpleMeterRegistry = new SimpleMeterRegistry() def 'Creating a timed aspect.'() { @@ -42,20 +40,4 @@ class MicroMeterConfigSpec extends Specification { assert objectUnderTest.processThreadMetrics() != null } - def 'Creating gauges for cm handle states.'() { - given: 'cache returns value for each state' - cmHandlesByState.get(_) >> 1 - when: 'gauges for each state are created' - objectUnderTest.advisedCmHandles(simpleMeterRegistry) - objectUnderTest.readyCmHandles(simpleMeterRegistry) - objectUnderTest.lockedCmHandles(simpleMeterRegistry) - objectUnderTest.deletingCmHandles(simpleMeterRegistry) - objectUnderTest.deletedCmHandles(simpleMeterRegistry) - then: 'each state has the correct value when queried' - ['ADVISED', 'READY', 'LOCKED', 'DELETING', 'DELETED'].each { state -> - def gaugeValue = simpleMeterRegistry.get(objectUnderTest.CM_HANDLE_STATE_GAUGE).tag('state',state).gauge().value() - assert gaugeValue == 1 - } - } - } 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 d7b38d1a46..6215427dc1 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 @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2021-2025 Nordix Foundation + * Modifications Copyright (C) 2021-2025 OpenInfra Foundation Europe * Modifications Copyright (C) 2021 highstreet technologies GmbH * Modifications Copyright (C) 2021-2022 Bell Canada * ================================================================================ @@ -262,10 +262,9 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final CmHandleQueryParameters cmHandleQueryParameters) { final CmHandleQueryApiParameters cmHandleQueryApiParameters = deprecationHelper.mapOldConditionProperties(cmHandleQueryParameters); - final Collection<NcmpServiceCmHandle> cmHandles = networkCmProxyInventoryFacade - .executeCmHandleSearch(cmHandleQueryApiParameters); final List<RestOutputCmHandle> restOutputCmHandles = - cmHandles.stream().map(this::toRestOutputCmHandle).collect(Collectors.toList()); + networkCmProxyInventoryFacade.executeCmHandleSearch(cmHandleQueryApiParameters) + .map(this::toRestOutputCmHandle).collectList().block(); return ResponseEntity.ok(restOutputCmHandles); } 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 c3aca5a99b..94c113c053 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 @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021 highstreet technologies GmbH - * Modifications Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2021-2025 OpenInfra Foundation Europe * Modifications Copyright (C) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -59,6 +59,7 @@ import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.test.web.servlet.MockMvc +import reactor.core.publisher.Flux import reactor.core.publisher.Mono import spock.lang.Shared import spock.lang.Specification @@ -275,7 +276,7 @@ class NetworkCmProxyControllerSpec extends Specification { cmHandle2.alternateId = 'someAlternateId' cmHandle2.moduleSetTag = 'someModuleSetTag' cmHandle2.dataProducerIdentifier = 'someDataProducerIdentifier' - mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> [cmHandle1, cmHandle2] + mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> Flux.fromIterable([cmHandle1, cmHandle2]) when: 'the searches api is invoked' def response = mvc.perform(post(searchesEndpoint).contentType(MediaType.APPLICATION_JSON).content(jsonString)).andReturn().response then: 'response status returns OK' @@ -352,7 +353,7 @@ class NetworkCmProxyControllerSpec extends Specification { cmHandle2.cmHandleId = 'ch-2' cmHandle2.publicProperties = [color: 'green'] cmHandle2.currentTrustLevel = TrustLevel.NONE - mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> [cmHandle1, cmHandle2] + mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> Flux.fromIterable([cmHandle1, cmHandle2]) when: 'the searches api is invoked' def response = mvc.perform(post(searchesEndpoint).contentType(MediaType.APPLICATION_JSON).content(jsonString)).andReturn().response then: 'an empty cm handle identifier is returned' diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java index 7e9ca7988b..2119f817e7 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation. + * Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,6 @@ package org.onap.cps.ncmp.api.datajobs.models; -import java.util.Map; - /** * Describes the write data job operation to be forwarded to dmi. * @@ -32,12 +30,10 @@ import java.util.Map; * @param moduleSetTag The module set tag of the CM Handle. * @param value The value to be written depends on the type of operation. * @param operationId Unique identifier of the operation within the request. - * @param privateProperties Contains the private properties of a Cm Handle. */ public record DmiWriteOperation( String path, String op, String moduleSetTag, Object value, - String operationId, - Map<String, String> privateProperties) {} + String operationId) {} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java index 9bfb775d55..876a5e7c40 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation + * Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import org.onap.cps.ncmp.api.inventory.models.CompositeState; import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration; import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistrationResponse; import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import reactor.core.publisher.Flux; public interface NetworkCmProxyInventoryFacade { @@ -96,9 +97,9 @@ public interface NetworkCmProxyInventoryFacade { * Retrieve cm handles with details for the given query parameters. * * @param cmHandleQueryApiParameters cm handle query parameters - * @return cm handles with details + * @return cm handle objects as a reactive stream (flux) */ - Collection<NcmpServiceCmHandle> executeCmHandleSearch(final CmHandleQueryApiParameters cmHandleQueryApiParameters); + Flux<NcmpServiceCmHandle> executeCmHandleSearch(final CmHandleQueryApiParameters cmHandleQueryApiParameters); /** * Retrieve cm handle ids for the given query parameters. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/CmHandleStateGaugeConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/CmHandleStateGaugeConfig.java new file mode 100644 index 0000000000..f63a1bf67c --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/CmHandleStateGaugeConfig.java @@ -0,0 +1,114 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * ================================================================================ + * 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.config; + +import com.hazelcast.map.IMap; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; + +@Configuration +@RequiredArgsConstructor +@DependsOn("cmHandleStateMonitor") +public class CmHandleStateGaugeConfig { + + private static final String STATE_TAG = "state"; + private static final String CM_HANDLE_STATE_GAUGE = "cps_ncmp_inventory_cm_handles_by_state"; + private final IMap<String, Integer> cmHandlesByState; + + /** + * Register gauge metric for cm handles with state 'advised'. + * + * @param meterRegistry meter registry + * @return cm handle state gauge + */ + @Bean + public Gauge advisedCmHandles(final MeterRegistry meterRegistry) { + return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, + value -> cmHandlesByState.get("advisedCmHandlesCount")) + .tag(STATE_TAG, "ADVISED") + .description("Current number of cm handles in advised state") + .register(meterRegistry); + } + + /** + * Register gauge metric for cm handles with state 'ready'. + * + * @param meterRegistry meter registry + * @return cm handle state gauge + */ + @Bean + public Gauge readyCmHandles(final MeterRegistry meterRegistry) { + return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, + value -> cmHandlesByState.get("readyCmHandlesCount")) + .tag(STATE_TAG, "READY") + .description("Current number of cm handles in ready state") + .register(meterRegistry); + } + + /** + * Register gauge metric for cm handles with state 'locked'. + * + * @param meterRegistry meter registry + * @return cm handle state gauge + */ + @Bean + public Gauge lockedCmHandles(final MeterRegistry meterRegistry) { + return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, + value -> cmHandlesByState.get("lockedCmHandlesCount")) + .tag(STATE_TAG, "LOCKED") + .description("Current number of cm handles in locked state") + .register(meterRegistry); + } + + /** + * Register gauge metric for cm handles with state 'deleting'. + * + * @param meterRegistry meter registry + * @return cm handle state gauge + */ + @Bean + public Gauge deletingCmHandles(final MeterRegistry meterRegistry) { + return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, + value -> cmHandlesByState.get("deletingCmHandlesCount")) + .tag(STATE_TAG, "DELETING") + .description("Current number of cm handles in deleting state") + .register(meterRegistry); + } + + /** + * Register gauge metric for cm handles with state 'deleted'. + * + * @param meterRegistry meter registry + * @return cm handle state gauge + */ + @Bean + public Gauge deletedCmHandles(final MeterRegistry meterRegistry) { + return Gauge.builder(CM_HANDLE_STATE_GAUGE, cmHandlesByState, + value -> cmHandlesByState.get("deletedCmHandlesCount")) + .tag(STATE_TAG, "DELETED") + .description("Number of cm handles that have been deleted since the application started") + .register(meterRegistry); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/NetworkCmProxyInventoryFacadeImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/NetworkCmProxyInventoryFacadeImpl.java index 118c2bba70..7130afdcfd 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/NetworkCmProxyInventoryFacadeImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/NetworkCmProxyInventoryFacadeImpl.java @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 highstreet technologies GmbH - * Modifications Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2021-2025 OpenInfra Foundation Europe * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada * Modifications Copyright (C) 2023 TechMahindra Ltd. @@ -52,6 +52,7 @@ import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher; import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; @Service @RequiredArgsConstructor @@ -118,15 +119,12 @@ public class NetworkCmProxyInventoryFacadeImpl implements NetworkCmProxyInventor } @Override - public Collection<NcmpServiceCmHandle> executeCmHandleSearch( + public Flux<NcmpServiceCmHandle> executeCmHandleSearch( final CmHandleQueryApiParameters cmHandleQueryApiParameters) { - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( - cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = + jsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); - final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles = - parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters); - trustLevelManager.applyEffectiveTrustLevels(ncmpServiceCmHandles); - return ncmpServiceCmHandles; + return parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters); } @Override diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/AsyncRestRequestResponseEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/DmiAsyncRequestResponseEventConsumer.java index f14bb15842..e2803e89a1 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/AsyncRestRequestResponseEventConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/DmiAsyncRequestResponseEventConsumer.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2023-2024 Nordix Foundation. + * Copyright (c) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ import org.springframework.stereotype.Component; @Slf4j @RequiredArgsConstructor @ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true) -public class AsyncRestRequestResponseEventConsumer { +public class DmiAsyncRequestResponseEventConsumer { private final EventsPublisher<NcmpAsyncRequestResponseEvent> eventsPublisher; private final NcmpAsyncRequestResponseEventMapper ncmpAsyncRequestResponseEventMapper; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java index f200aa2ad7..f189edc89a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2024-2025 Nordix Foundation + * Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ package org.onap.cps.ncmp.impl.datajobs; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -107,7 +106,6 @@ public class WriteRequestExaminer { writeOperation.op(), ncmpServiceCmHandle.getModuleSetTag(), writeOperation.value(), - writeOperation.operationId(), - Collections.emptyMap()); // TODO: Private properties will be removed as part of CPS-2693. + writeOperation.operationId()); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java index d6b947697e..15aa1213aa 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2025 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -130,4 +130,13 @@ public interface CmHandleQueryService { */ Collection<String> getAllCmHandleReferences(boolean outputAlternateId); + /** + * Retrieves all Cm handle references by cps path. + * + * @param cpsPath cps path for which the cmHandle is requested + * @param outputAlternateId If {@code true}, returns alternate ids; if {@code false}, returns standard cm handle ids + * @return collection of cm handle references. Returns an empty collection if no references are found. + */ + Collection<String> getCmHandleReferencesByCpsPath(String cpsPath, boolean outputAlternateId); + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java index 4a753bd8f4..bdf3785a7a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2025 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -152,6 +152,25 @@ public class CmHandleQueryServiceImpl implements CmHandleQueryService { return cpsQueryService.queryDataLeaf(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cpsPath, String.class); } + @Override + public Collection<String> getCmHandleReferencesByCpsPath(final String cpsPath, final boolean outputAlternateId) { + final String cpsPathInQuery; + final String cpsPathInQueryWithAttribute; + if (CpsPathUtil.getCpsPathQuery(cpsPath).getXpathPrefix().endsWith("/cm-handles")) { + cpsPathInQuery = cpsPath; + } else { + cpsPathInQuery = cpsPath + ANCESTOR_CM_HANDLES; + } + + if (outputAlternateId) { + cpsPathInQueryWithAttribute = cpsPathInQuery + "/@alternate-id"; + } else { + cpsPathInQueryWithAttribute = cpsPathInQuery + "/@id"; + } + return cpsQueryService.queryDataLeaf(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + cpsPathInQueryWithAttribute, String.class); + } + private Collection<String> getCmHandleReferencesByTrustLevel(final TrustLevel targetTrustLevel, final boolean outputAlternateId) { final Collection<String> selectedCmHandleReferences = new HashSet<>(); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java index fc8884433c..5a105b347a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ package org.onap.cps.ncmp.impl.inventory; import java.util.Collection; import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters; import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import reactor.core.publisher.Flux; public interface ParameterizedCmHandleQueryService { /** @@ -31,6 +32,7 @@ public interface ParameterizedCmHandleQueryService { * public properties * modules * cps-path + * trust level * * @param cmHandleQueryServiceParameters the cm handle query parameters * @param outputAlternateId Boolean for cm handle reference type either @@ -62,19 +64,12 @@ public interface ParameterizedCmHandleQueryService { * public properties * modules * cps-path + * trust level * * @param cmHandleQueryServiceParameters the cm handle query parameters - * @return collection of cm handles + * @return cm handle objects as a reactive stream (flux) */ - Collection<NcmpServiceCmHandle> 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<NcmpServiceCmHandle> getAllCmHandles(); + Flux<NcmpServiceCmHandle> queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); /** * Retrieves all {@code NcmpServiceCmHandle} instances without their associated properties. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java index 9ce0e04ed2..6076895f0f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2025 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ package org.onap.cps.ncmp.impl.inventory; import static org.onap.cps.api.parameters.FetchDescendantsOption.DIRECT_CHILDREN_ONLY; -import static org.onap.cps.api.parameters.FetchDescendantsOption.OMIT_DESCENDANTS; import static org.onap.cps.ncmp.impl.inventory.CmHandleQueryParametersValidator.validateCpsPathConditionProperties; import static org.onap.cps.ncmp.impl.inventory.CmHandleQueryParametersValidator.validateModuleNameConditionProperties; import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; @@ -49,16 +48,20 @@ import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; import org.onap.cps.ncmp.impl.inventory.models.InventoryQueryConditions; import org.onap.cps.ncmp.impl.inventory.models.PropertyType; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager; import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; @Service @RequiredArgsConstructor public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHandleQueryService { + private static final int FLUX_BUFFER_SIZE = 1000; private static final Collection<String> NO_QUERY_TO_EXECUTE = null; private final CmHandleQueryService cmHandleQueryService; private final InventoryPersistence inventoryPersistence; + private final TrustLevelManager trustLevelManager; @Override public Collection<String> queryCmHandleReferenceIds( @@ -83,24 +86,12 @@ public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHan } @Override - public Collection<NcmpServiceCmHandle> queryCmHandles( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - - if (cmHandleQueryServiceParameters.getCmHandleQueryParameters().isEmpty()) { - return getAllCmHandles(); - } - - final Collection<String> cmHandleIds = queryCmHandleReferenceIds(cmHandleQueryServiceParameters, false); - + public Flux<NcmpServiceCmHandle> queryCmHandles(final CmHandleQueryServiceParameters queryParameters) { + final Collection<String> cmHandleIds = queryCmHandleReferenceIds(queryParameters, false); return getNcmpServiceCmHandles(cmHandleIds); } @Override - public Collection<NcmpServiceCmHandle> getAllCmHandles() { - return toNcmpServiceCmHandles(inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT)); - } - - @Override public Collection<NcmpServiceCmHandle> getAllCmHandlesWithoutProperties() { return toNcmpServiceCmHandles(inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT, DIRECT_CHILDREN_ONLY)); } @@ -189,9 +180,8 @@ public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHan return NO_QUERY_TO_EXECUTE; } try { - cpsPathQueryResult = collectCmHandleReferencesFromDataNodes( - cmHandleQueryService.queryCmHandleAncestorsByCpsPath(cpsPathCondition.get("cpsPath"), - OMIT_DESCENDANTS), outputAlternateId); + cpsPathQueryResult = cmHandleQueryService.getCmHandleReferencesByCpsPath(cpsPathCondition.get("cpsPath"), + outputAlternateId); } catch (final PathParsingException pathParsingException) { throw new DataValidationException(pathParsingException.getMessage(), pathParsingException.getDetails(), pathParsingException); @@ -236,7 +226,14 @@ public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHan return cmHandleQueryService.getAllCmHandleReferences(outputAlternateId); } - private Collection<NcmpServiceCmHandle> getNcmpServiceCmHandles(final Collection<String> cmHandleIds) { + private Flux<NcmpServiceCmHandle> getNcmpServiceCmHandles(final Collection<String> cmHandleIds) { + return Flux.fromIterable(cmHandleIds) + .buffer(FLUX_BUFFER_SIZE) + .map(this::getNcmpServiceCmHandleBatch) + .flatMap(Flux::fromIterable); + } + + private Collection<NcmpServiceCmHandle> getNcmpServiceCmHandleBatch(final Collection<String> cmHandleIds) { final Collection<YangModelCmHandle> yangModelcmHandles = inventoryPersistence.getYangModelCmHandles(cmHandleIds); @@ -245,6 +242,7 @@ public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHan yangModelcmHandles.forEach(yangModelcmHandle -> ncmpServiceCmHandles.add(YangDataConverter.toNcmpServiceCmHandle(yangModelcmHandle)) ); + trustLevelManager.applyEffectiveTrustLevels(ncmpServiceCmHandles); return ncmpServiceCmHandles; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/CmHandleStateMonitor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/CmHandleStateMonitor.java index 708508e9d8..3d8e8b6e31 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/CmHandleStateMonitor.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/CmHandleStateMonitor.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2025 Nordix Foundation. + * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,12 +31,14 @@ import org.onap.cps.ncmp.api.inventory.models.CompositeState; import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService; import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventsCmHandleStateHandlerImpl.CmHandleTransitionPair; import org.onap.cps.ncmp.utils.events.NcmpInventoryModelOnboardingFinishedEvent; +import org.springframework.context.annotation.DependsOn; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor +@DependsOn("adminCacheConfig") @Slf4j public class CmHandleStateMonitor { private static final String METRIC_POSTFIX = "CmHandlesCount"; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java index a53c902683..9d5bc1575a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023-2024 Nordix Foundation + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ import org.springframework.stereotype.Service; public class LcmEventsCmHandleStateHandlerAsyncHelper { private final LcmEventsCreator lcmEventsCreator; - private final LcmEventsService lcmEventsService; + private final LcmEventsProducer lcmEventsProducer; /** * Publish LcmEvent in batches and in asynchronous manner. @@ -58,7 +58,7 @@ public class LcmEventsCmHandleStateHandlerAsyncHelper { existingNcmpServiceCmHandle); final LcmEvent lcmEvent = lcmEventsCreator.populateLcmEvent(cmHandleId, targetNcmpServiceCmHandle, existingNcmpServiceCmHandle); - lcmEventsService.publishLcmEvent(cmHandleId, lcmEvent, lcmEventHeader); + lcmEventsProducer.publishLcmEvent(cmHandleId, lcmEvent, lcmEventHeader); } private static NcmpServiceCmHandle toNcmpServiceCmHandle(final YangModelCmHandle yangModelCmHandle) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsProducer.java index 192667175e..d62688de1d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsProducer.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,16 +38,16 @@ import org.springframework.kafka.KafkaException; import org.springframework.stereotype.Service; /** - * LcmEventsService to call the publisher and publish on the dedicated topic. + * LcmEventsProducer to call the publisher and publish on the dedicated topic. */ @Slf4j @Service @RequiredArgsConstructor -public class LcmEventsService { +public class LcmEventsProducer { private static final Tag TAG_METHOD = Tag.of("method", "publishLcmEvent"); - private static final Tag TAG_CLASS = Tag.of("class", LcmEventsService.class.getName()); + private static final Tag TAG_CLASS = Tag.of("class", LcmEventsProducer.class.getName()); private static final String UNAVAILABLE_CM_HANDLE_STATE = "N/A"; private final EventsPublisher<LcmEvent> eventsPublisher; private final JsonObjectMapper jsonObjectMapper; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java index 692bf5caee..27ad535344 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023-2024 Nordix Foundation + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ import org.onap.cps.ncmp.impl.dmi.DmiServiceNameResolver; import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.ncmp.impl.models.RequiredDmiService; -import org.onap.cps.ncmp.utils.events.CmAvcEventPublisher; +import org.onap.cps.ncmp.utils.events.InventoryEventProducer; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @@ -52,7 +52,7 @@ public class TrustLevelManager { private final IMap<String, TrustLevel> trustLevelPerDmiPlugin; private final InventoryPersistence inventoryPersistence; - private final CmAvcEventPublisher cmAvcEventPublisher; + private final InventoryEventProducer inventoryEventProducer; private static final String AVC_CHANGED_ATTRIBUTE_NAME = "trustLevel"; private static final String AVC_NO_OLD_VALUE = null; @@ -82,7 +82,7 @@ public class TrustLevelManager { } trustLevelPerCmHandleIdForCache.put(cmHandleId, initialTrustLevel); if (TrustLevel.NONE.equals(initialTrustLevel)) { - cmAvcEventPublisher.publishAvcEvent(cmHandleId, + inventoryEventProducer.publishAvcEvent(cmHandleId, AVC_CHANGED_ATTRIBUTE_NAME, AVC_NO_OLD_VALUE, initialTrustLevel.name()); @@ -197,7 +197,7 @@ public class TrustLevelManager { } else { log.info("The trust level for Cm Handle: {} is now: {} ", notificationCandidateCmHandleId, newEffectiveTrustLevel); - cmAvcEventPublisher.publishAvcEvent(notificationCandidateCmHandleId, + inventoryEventProducer.publishAvcEvent(notificationCandidateCmHandleId, AVC_CHANGED_ATTRIBUTE_NAME, oldEffectiveTrustLevel.name(), newEffectiveTrustLevel.name()); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CmAvcEventPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/InventoryEventProducer.java index 2bb35864d3..f388ee1b20 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CmAvcEventPublisher.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/InventoryEventProducer.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023-2025 Nordix Foundation + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor -public class CmAvcEventPublisher { +public class InventoryEventProducer { private final EventsPublisher<CloudEvent> eventsPublisher; diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/CmHandleStateGaugeConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/CmHandleStateGaugeConfigSpec.groovy new file mode 100644 index 0000000000..499f8b8e54 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/CmHandleStateGaugeConfigSpec.groovy @@ -0,0 +1,75 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * ================================================================================ + * 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.config + +import com.hazelcast.map.IMap +import io.micrometer.core.instrument.MeterRegistry +import io.micrometer.core.instrument.simple.SimpleMeterRegistry +import org.onap.cps.ncmp.impl.cache.AdminCacheConfig +import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService +import org.onap.cps.ncmp.impl.inventory.sync.lcm.CmHandleStateMonitor +import org.spockframework.spring.SpringBean +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.context.TestPropertySource +import spock.lang.Specification + +@SpringBootTest(classes = [CmHandleStateGaugeConfig, CmHandleStateMonitor, AdminCacheConfig]) +@ContextConfiguration(classes = [CpsApplicationContext]) +@TestPropertySource(properties = ["hazelcast.mode.kubernetes.enabled=false"]) +class CmHandleStateGaugeConfigSpec extends Specification { + + @Autowired + CpsApplicationContext cpsApplicationContext + @SpringBean + CmHandleQueryService cmHandleQueryService = Mock() + @SpringBean + MeterRegistry meterRegistry = Mock() + + def cmHandlesByState = Mock(IMap) + def objectUnderTest = new CmHandleStateGaugeConfig(cmHandlesByState) + def simpleMeterRegistry = new SimpleMeterRegistry() + + def 'Creating gauges for cm handle states.'() { + given: 'cache returns a test value (123) for each state' + cmHandlesByState.get(_) >> 123 + when: 'gauges for each state are created' + objectUnderTest.advisedCmHandles(simpleMeterRegistry) + objectUnderTest.readyCmHandles(simpleMeterRegistry) + objectUnderTest.lockedCmHandles(simpleMeterRegistry) + objectUnderTest.deletingCmHandles(simpleMeterRegistry) + objectUnderTest.deletedCmHandles(simpleMeterRegistry) + then: 'each state has the correct value when queried' + ['ADVISED', 'READY', 'LOCKED', 'DELETING', 'DELETED'].each { state -> + def gaugeValue = simpleMeterRegistry.get(objectUnderTest.CM_HANDLE_STATE_GAUGE).tag('state',state).gauge().value() + assert gaugeValue == 123 + } + } + + def 'Controlling order of bean initialization'() { + when: 'cm handle state gauge config is retrieved' + cpsApplicationContext.getCpsBean(CmHandleStateGaugeConfig.class) + then: 'cm handle state monitor should already be available' + cpsApplicationContext.getCpsBean(CmHandleStateMonitor.class) != null + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducerSpec.groovy index 3bf4c2c160..49e43f9067 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducerSpec.groovy @@ -22,6 +22,7 @@ package org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi import com.fasterxml.jackson.databind.ObjectMapper import io.cloudevents.CloudEvent +import io.cloudevents.core.v1.CloudEventBuilder import org.onap.cps.events.EventsPublisher import org.onap.cps.ncmp.config.CpsApplicationContext import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.CmHandle @@ -33,7 +34,7 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ContextConfiguration import spock.lang.Specification -@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) +@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper, CloudEventBuilder]) @ContextConfiguration(classes = [CpsApplicationContext]) class DmiInEventProducerSpec extends Specification { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy index fde7e182d0..d8adde261c 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy @@ -22,6 +22,7 @@ package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp import com.fasterxml.jackson.databind.ObjectMapper import io.cloudevents.CloudEvent +import io.cloudevents.core.v1.CloudEventBuilder import org.onap.cps.events.EventsPublisher import org.onap.cps.ncmp.config.CpsApplicationContext import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler @@ -33,7 +34,7 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ContextConfiguration import spock.lang.Specification -@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) +@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper, CloudEventBuilder]) @ContextConfiguration(classes = [CpsApplicationContext]) class NcmpOutEventProducerSpec extends Specification { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy index 4bcafe8c61..c651bb5d0f 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2022-2024 Nordix Foundation. + * Copyright (c) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,10 +35,9 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.annotation.DirtiesContext import org.testcontainers.spock.Testcontainers - import java.time.Duration -@SpringBootTest(classes = [EventsPublisher, AsyncRestRequestResponseEventConsumer, ObjectMapper, JsonObjectMapper]) +@SpringBootTest(classes = [EventsPublisher, DmiAsyncRequestResponseEventConsumer, ObjectMapper, JsonObjectMapper]) @Testcontainers @DirtiesContext class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends MessagingBaseSpec { @@ -53,8 +52,8 @@ class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends MessagingBase Mappers.getMapper(NcmpAsyncRequestResponseEventMapper.class) @SpringBean - AsyncRestRequestResponseEventConsumer ncmpAsyncRequestResponseEventConsumer = - new AsyncRestRequestResponseEventConsumer(cpsAsyncRequestResponseEventPublisher, + DmiAsyncRequestResponseEventConsumer dmiAsyncRequestResponseEventConsumer = + new DmiAsyncRequestResponseEventConsumer(cpsAsyncRequestResponseEventPublisher, ncmpAsyncRequestResponseEventMapper) @Autowired @@ -69,7 +68,7 @@ class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends MessagingBase def jsonData = TestUtils.getResourceFileContent('dmiAsyncRequestResponseEvent.json') def testEventSent = jsonObjectMapper.convertJsonString(jsonData, DmiAsyncRequestResponseEvent.class) when: 'the event is consumed' - ncmpAsyncRequestResponseEventConsumer.consumeAndForward(testEventSent) + dmiAsyncRequestResponseEventConsumer.consumeAndForward(testEventSent) and: 'the topic is polled' def records = legacyEventKafkaConsumer.poll(Duration.ofMillis(1500)) then: 'poll returns one record' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/FilterStrategiesIntegrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/FilterStrategiesIntegrationSpec.groovy index 01d2a3666b..8039d4767c 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/FilterStrategiesIntegrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/FilterStrategiesIntegrationSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2023-2024 Nordix Foundation. + * Copyright (c) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ import spock.util.concurrent.PollingConditions import java.util.concurrent.TimeUnit -@SpringBootTest(classes =[DataOperationEventConsumer, AsyncRestRequestResponseEventConsumer, RecordFilterStrategies, KafkaConfig]) +@SpringBootTest(classes =[DataOperationEventConsumer, DmiAsyncRequestResponseEventConsumer, RecordFilterStrategies, KafkaConfig]) @DirtiesContext @Testcontainers @EnableAutoConfiguration diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/SerializationIntegrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/SerializationIntegrationSpec.groovy index 3fe7ec222e..75738b443f 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/SerializationIntegrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/async/SerializationIntegrationSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2023-2024 Nordix Foundation. + * Copyright (c) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ import org.springframework.test.annotation.DirtiesContext import org.testcontainers.spock.Testcontainers import spock.util.concurrent.PollingConditions -@SpringBootTest(classes =[DataOperationEventConsumer, AsyncRestRequestResponseEventConsumer, RecordFilterStrategies, KafkaConfig]) +@SpringBootTest(classes =[DataOperationEventConsumer, DmiAsyncRequestResponseEventConsumer, RecordFilterStrategies, KafkaConfig]) @DirtiesContext @Testcontainers @EnableAutoConfiguration diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy index 93362f23be..175fb1877b 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy @@ -25,13 +25,13 @@ class DmiSubJobRequestHandlerSpec extends Specification { given: 'a data job id, metadata and a map of producer keys and write operations to create a request' def dataJobId = 'some-job-id' def dataJobMetadata = new DataJobMetadata('d1', 't1', 't2') - def dmiWriteOperation = new DmiWriteOperation('p', 'operation', 'tag', null, 'o1', [:]) - def dmiWriteOperationsPerProducerKey = [new ProducerKey('dmi1', 'prod1'): [dmiWriteOperation]] + def dmiWriteOperation = new DmiWriteOperation('p', 'operation', 'tag', null, 'o1') + def dmiWriteOperationsPerProducerKey = [(new ProducerKey('dmi1', 'prod1')): [dmiWriteOperation]] def authorization = 'my authorization header' and: 'the dmi rest client will return a response (for the correct parameters)' def responseAsKeyValuePairs = [subJobId:'my-sub-job-id'] def responseEntity = new ResponseEntity<>(responseAsKeyValuePairs, HttpStatus.OK) - def expectedJson = '{"destination":"d1","dataAcceptType":"t1","dataContentType":"t2","dataProducerId":"prod1","dataJobId":"some-job-id","data":[{"path":"p","op":"operation","moduleSetTag":"tag","value":null,"operationId":"o1","privateProperties":{}}]}' + def expectedJson = '{"destination":"d1","dataAcceptType":"t1","dataContentType":"t2","dataProducerId":"prod1","dataJobId":"some-job-id","data":[{"path":"p","op":"operation","moduleSetTag":"tag","value":null,"operationId":"o1"}]}' mockDmiRestClient.synchronousPostOperationWithJsonData(RequiredDmiService.DATA, _, expectedJson, OperationType.CREATE, authorization) >> responseEntity when: 'sending request to DMI invoked' objectUnderTest.sendRequestsToDmi(authorization, dataJobId, dataJobMetadata, dmiWriteOperationsPerProducerKey) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy index 57210c5e50..e978121644 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2025 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -234,6 +234,20 @@ class CmHandleQueryServiceImplSpec extends Specification { 'output is cm handle ids' | false || ['PNFDemo', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4', 'PNFDemo5'] } + def 'Get all cm handle references by cps path'() { + when: 'the all cm handle references is retrieved via cps path' + objectUnderTest.getCmHandleReferencesByCpsPath(sampleCpsPath, outputAlternateId) + then: 'query service to query data leaf is called once with the correct cps path as parameter' + 1 * mockCpsQueryService.queryDataLeaf(_, _, expectedCpsPathForQuery,_) + where: + scenario | sampleCpsPath | outputAlternateId || expectedCpsPathForQuery + 'cps path suffixes with cm-handles and outputs alternateId' | '/some/path/ending/in/cm-handles' | true || '/some/path/ending/in/cm-handles/@alternate-id' + 'cps path suffixes without cm-handles and outputs alternateId'| '/some/path/NotEnding/incmhandles'| true || '/some/path/NotEnding/incmhandles/ancestor::cm-handles/@alternate-id' + 'cps path suffixes with cm-handles and outputs cmHandleId' | '/some/path/ending/in/cm-handles' | false || '/some/path/ending/in/cm-handles/@id' + 'cps path suffixes without cm-handles and outputs cmhandleId' | '/some/path/NotEnding/incmhandles'| false || '/some/path/NotEnding/incmhandles/ancestor::cm-handles/@id' + + } + void mockResponses() { mockCpsQueryService.queryDataLeaf(_, _, '//public-properties[@name=\'Contact\' and @value=\'newemailforstore@bookstore.com\']/ancestor::cm-handles/@id', _) >> [pnfDemo.getLeaves().get('id'), pnfDemo2.getLeaves().get('id'), pnfDemo4.getLeaves().get('id')] @@ -261,6 +275,7 @@ class CmHandleQueryServiceImplSpec extends Specification { mockCpsQueryService.queryDataLeaf(_, _, '/dmi-registry/cm-handles/@alternate-id', _) >> getAllCmHandleReferences(true) mockCpsQueryService.queryDataLeaf(_, _, '/dmi-registry/cm-handles/@id', _) >> getAllCmHandleReferences(false) + } def static createDataNode(dataNodeId) { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy index eff8082a0d..29cd92db3f 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada * Modifications Copyright (C) 2023 TechMahindra Ltd. @@ -40,6 +40,7 @@ import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher import org.onap.cps.utils.JsonObjectMapper +import reactor.core.publisher.Flux import spock.lang.Specification class NetworkCmProxyInventoryFacadeSpec extends Specification { @@ -249,11 +250,10 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification { and: 'query cm handle method returns two cm handles' mockParameterizedCmHandleQueryService.queryCmHandles( spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class)) - >> [new NcmpServiceCmHandle(cmHandleId: 'ch-0'), new NcmpServiceCmHandle(cmHandleId: 'ch-1')] - and: 'a trust level for cm handles' - 1 * mockTrustLevelManager.applyEffectiveTrustLevels(_) >> { args -> args[0].forEach{it.currentTrustLevel = TrustLevel.COMPLETE } } + >> Flux.fromIterable([new NcmpServiceCmHandle(cmHandleId: 'ch-0', currentTrustLevel: TrustLevel.COMPLETE), + new NcmpServiceCmHandle(cmHandleId: 'ch-1', currentTrustLevel: TrustLevel.COMPLETE)]) when: 'execute cm handle search is called' - def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters) + def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters).collectList().block() then: 'result consists of the two cm handles returned by the CPS Data Service' assert result.size() == 2 assert result[0].cmHandleId == 'ch-0' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy index 8bb4551f79..594d7fb31d 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2025 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.onap.cps.api.exceptions.DataInUseException import org.onap.cps.api.exceptions.DataValidationException import org.onap.cps.api.model.ConditionProperties import org.onap.cps.api.model.DataNode +import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager import spock.lang.Specification import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT @@ -38,27 +39,28 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification { def cmHandleQueries = Mock(CmHandleQueryService) def partiallyMockedCmHandleQueries = Spy(CmHandleQueryService) def mockInventoryPersistence = Mock(InventoryPersistence) + def mockTrustLevelManager = Mock(TrustLevelManager) 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 objectUnderTest = new ParameterizedCmHandleQueryServiceImpl(cmHandleQueries, mockInventoryPersistence, mockTrustLevelManager) + def objectUnderTestWithPartiallyMockedQueries = new ParameterizedCmHandleQueryServiceImpl(partiallyMockedCmHandleQueries, mockInventoryPersistence, mockTrustLevelManager) 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', 'alternate-id':'some-alternate-id'])] + and: 'the query get the cm handle references' + cmHandleQueries.getCmHandleReferencesByCpsPath('/some/cps/path', outputAlternateId) >> cmHandleReferences.asCollection() when: 'the query is executed for cm handle ids' def result = objectUnderTest.queryCmHandleReferenceIds(cmHandleQueryParameters, outputAlternateId) then: 'the correct expected cm handles ids are returned' assert result == expectedCmhandleReference where: 'the following data is used' - senario | outputAlternateId || expectedCmhandleReference - 'output CmHandle Ids' | false || ['some-cmhandle-id'] as Set - 'output Alternate Ids' | true || ['some-alternate-id'] as Set + senario | outputAlternateId | cmHandleReferences || expectedCmhandleReference + 'output CmHandle Ids' | false | ['some-cmhandle-id'] as Set || ['some-cmhandle-id'] as Set + 'output Alternate Ids' | true | ['some-alternate-id'] as Set || ['some-alternate-id'] as Set } def 'Query cm handle where cps path itself is ancestor axis.'() { @@ -66,16 +68,16 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification { def cmHandleQueryParameters = new CmHandleQueryServiceParameters() def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']]) cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties]) - and: 'the query get the cm handle data nodes excluding all descendants returns a datanode' - cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id', 'alternate-id':'some-alternate-id'])] + and: 'the query get the cm handle references' + cmHandleQueries.getCmHandleReferencesByCpsPath('/some/cps/path', outputAlternateId) >> cmHandleReferences.asCollection() when: 'the query is executed for cm handle ids' def result = objectUnderTest.queryCmHandleIdsForInventory(cmHandleQueryParameters, outputAlternateId) then: 'the correct expected cm handles ids are returned' assert result == expectedCmhandleReference where: 'the following data is used' - senario | outputAlternateId || expectedCmhandleReference - 'outputAlternate is false' | false || ['some-cmhandle-id'] as Set - 'outputAlternate is true' | true || ['some-alternate-id'] as Set + senario | outputAlternateId | cmHandleReferences || expectedCmhandleReference + 'outputAlternate is false' | false | ['some-cmhandle-id'] as Set || ['some-cmhandle-id'] as Set + 'outputAlternate is true' | true | ['some-alternate-id'] as Set|| ['some-alternate-id'] as Set } def 'Cm handle ids query with error: #scenario.'() { @@ -84,7 +86,7 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification { 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 } + cmHandleQueries.getCmHandleReferencesByCpsPath('/some/cps/path', _) >> { throw thrownException } when: 'the query is executed for cm handle ids' objectUnderTest.queryCmHandleReferenceIds(cmHandleQueryParameters, false) then: 'a data validation exception is thrown' @@ -141,7 +143,7 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification { 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) + def result = objectUnderTest.queryCmHandles(cmHandleQueryParameters).collectList().block() then: 'the inventory service is called with the correct module names' 1 * mockInventoryPersistence.getCmHandleReferencesWithGivenModules(['some-module-name'], false) >> ['ch1'] and: 'the inventory service is called with teh correct if and returns a yang model cm handle' @@ -171,10 +173,12 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification { 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] + and: 'the inventory persistence returns the cm handle ids of all cm handles' + cmHandleQueries.getAllCmHandleReferences(false) >> getCmHandleReferencesForDmiRegistry(false) + and: 'the inventory persistence returns the cm handle details when requested' + mockInventoryPersistence.getYangModelCmHandles(_) >> dmiRegistry.childDataNodes.collect { new YangModelCmHandle(id: it.leaves.get("id").toString(), dmiProperties: [], publicProperties: []) } when: 'the query is executed for both cm handle details' - def result = objectUnderTest.queryCmHandles(cmHandleQueryParameters) + def result = objectUnderTest.queryCmHandles(cmHandleQueryParameters).collectList().block() then: 'the correct cm handles are returned' assert result.size() == 4 assert result.cmHandleId.containsAll('PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4') diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy index 62db2e34ad..73b5948432 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2025 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,10 +55,10 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { def mockInventoryPersistence = Mock(InventoryPersistence) def mockLcmEventsCreator = Mock(LcmEventsCreator) - def mockLcmEventsService = Mock(LcmEventsService) + def mockLcmEventsProducer = Mock(LcmEventsProducer) def mockCmHandleStateMonitor = Mock(CmHandleStateMonitor) - def lcmEventsCmHandleStateHandlerAsyncHelper = new LcmEventsCmHandleStateHandlerAsyncHelper(mockLcmEventsCreator, mockLcmEventsService) + def lcmEventsCmHandleStateHandlerAsyncHelper = new LcmEventsCmHandleStateHandlerAsyncHelper(mockLcmEventsCreator, mockLcmEventsProducer) def objectUnderTest = new LcmEventsCmHandleStateHandlerImpl(mockInventoryPersistence, lcmEventsCmHandleStateHandlerAsyncHelper, mockCmHandleStateMonitor) def cmHandleId = 'cmhandle-id-1' @@ -83,7 +83,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { assert loggingEvent.level == Level.INFO assert loggingEvent.formattedMessage == "${cmHandleId} is now in ${toCmHandleState} state" and: 'event service is called to publish event' - 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _) + 1 * mockLcmEventsProducer.publishLcmEvent(cmHandleId, _, _) where: 'state change parameters are provided' stateChange | fromCmHandleState | toCmHandleState 'ADVISED to READY' | ADVISED | READY @@ -100,7 +100,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { then: 'CM-handle is saved using inventory persistence' 1 * mockInventoryPersistence.saveCmHandleBatch(List.of(yangModelCmHandle)) and: 'event service is called to publish event' - 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _) + 1 * mockLcmEventsProducer.publishLcmEvent(cmHandleId, _, _) and: 'a log entry is written' assert getLogMessage(0) == "${cmHandleId} is now in ADVISED state" } @@ -120,7 +120,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { } } and: 'event service is called to publish event' - 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _) + 1 * mockLcmEventsProducer.publishLcmEvent(cmHandleId, _, _) and: 'a log entry is written' assert getLogMessage(0) == "${cmHandleId} is now in ADVISED state" } @@ -142,7 +142,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { } } and: 'event service is called to publish event' - 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _) + 1 * mockLcmEventsProducer.publishLcmEvent(cmHandleId, _, _) and: 'a log entry is written' assert getLogMessage(0) == "${cmHandleId} is now in READY state" } @@ -158,7 +158,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { and: 'method to persist cm handle state is called once' 1 * mockInventoryPersistence.saveCmHandleStateBatch(Map.of(yangModelCmHandle.getId(), yangModelCmHandle.getCompositeState())) and: 'the method to publish Lcm event is called once' - 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _) + 1 * mockLcmEventsProducer.publishLcmEvent(cmHandleId, _, _) } def 'Update cmHandle state to DELETING to DELETED' (){ @@ -170,7 +170,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { then: 'the cm handle state is as expected' yangModelCmHandle.getCompositeState().getCmHandleState() == DELETED and: 'the method to publish Lcm event is called once' - 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _) + 1 * mockLcmEventsProducer.publishLcmEvent(cmHandleId, _, _) } def 'No state change and no event to be published'() { @@ -182,7 +182,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { 1 * mockInventoryPersistence.saveCmHandleBatch(EMPTY_LIST) 1 * mockInventoryPersistence.saveCmHandleStateBatch(EMPTY_MAP) and: 'no event will be published' - 0 * mockLcmEventsService.publishLcmEvent(*_) + 0 * mockLcmEventsProducer.publishLcmEvent(*_) and: 'no log entries are written' assert logger.list.empty } @@ -201,7 +201,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { and: 'no state updates are persisted' 1 * mockInventoryPersistence.saveCmHandleStateBatch(EMPTY_MAP) and: 'event service is called to publish events' - 2 * mockLcmEventsService.publishLcmEvent(_, _, _) + 2 * mockLcmEventsProducer.publishLcmEvent(_, _, _) and: 'two log entries are written' assert getLogMessage(0) == 'cmhandle1 is now in ADVISED state' assert getLogMessage(1) == 'cmhandle2 is now in ADVISED state' @@ -221,7 +221,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { and: 'no new handles are persisted' 1 * mockInventoryPersistence.saveCmHandleBatch(EMPTY_LIST) and: 'event service is called to publish events' - 2 * mockLcmEventsService.publishLcmEvent(_, _, _) + 2 * mockLcmEventsProducer.publishLcmEvent(_, _, _) and: 'two log entries are written' assert getLogMessage(0) == 'cmhandle1 is now in READY state' assert getLogMessage(1) == 'cmhandle2 is now in DELETING state' @@ -237,7 +237,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { and: 'no new handles are persisted' 1 * mockInventoryPersistence.saveCmHandleBatch(EMPTY_LIST) and: 'event service is called to publish events' - 2 * mockLcmEventsService.publishLcmEvent(_, _, _) + 2 * mockLcmEventsProducer.publishLcmEvent(_, _, _) and: 'two log entries are written' assert getLogMessage(0) == 'cmhandle1 is now in DELETED state' assert getLogMessage(1) == 'cmhandle2 is now in DELETED state' @@ -253,7 +253,7 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification { then: 'the exception is not handled' thrown(RuntimeException) and: 'no events are published' - 0 * mockLcmEventsService.publishLcmEvent(_, _, _) + 0 * mockLcmEventsProducer.publishLcmEvent(_, _, _) and: 'no log entries are written' assert logger.list.empty } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsProducerSpec.groovy index 73c66089a3..a0b6de1aa4 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsProducerSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,13 +34,13 @@ import org.onap.cps.utils.JsonObjectMapper import org.springframework.kafka.KafkaException import spock.lang.Specification -class LcmEventsServiceSpec extends Specification { +class LcmEventsProducerSpec extends Specification { def mockLcmEventsPublisher = Mock(EventsPublisher) def mockJsonObjectMapper = Mock(JsonObjectMapper) def meterRegistry = new SimpleMeterRegistry() - def objectUnderTest = new LcmEventsService(mockLcmEventsPublisher, mockJsonObjectMapper, meterRegistry) + def objectUnderTest = new LcmEventsProducer(mockLcmEventsPublisher, mockJsonObjectMapper, meterRegistry) def 'Create and Publish lcm event where events are #scenario'() { given: 'a cm handle id, Lcm Event, and headers' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy index 1ab517cdcf..72ca190ff1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023-2024 Nordix Foundation + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle import org.onap.cps.ncmp.api.inventory.models.TrustLevel import org.onap.cps.ncmp.impl.inventory.InventoryPersistence import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle -import org.onap.cps.ncmp.utils.events.CmAvcEventPublisher +import org.onap.cps.ncmp.utils.events.InventoryEventProducer import spock.lang.Specification class TrustLevelManagerSpec extends Specification { @@ -39,13 +39,13 @@ class TrustLevelManagerSpec extends Specification { IMap<String, TrustLevel> trustLevelPerDmiPlugin def mockInventoryPersistence = Mock(InventoryPersistence) - def mockAttributeValueChangeEventPublisher = Mock(CmAvcEventPublisher) + def mockInventoryEventProducer = Mock(InventoryEventProducer) def setup() { hazelcastInstance = Hazelcast.newHazelcastInstance() trustLevelPerCmHandleId = hazelcastInstance.getMap("trustLevelPerCmHandle") trustLevelPerDmiPlugin = hazelcastInstance.getMap("trustLevelPerCmHandle") - objectUnderTest = new TrustLevelManager(trustLevelPerCmHandleId, trustLevelPerDmiPlugin, mockInventoryPersistence, mockAttributeValueChangeEventPublisher) + objectUnderTest = new TrustLevelManager(trustLevelPerCmHandleId, trustLevelPerDmiPlugin, mockInventoryPersistence, mockInventoryEventProducer) } def cleanup() { @@ -71,7 +71,7 @@ class TrustLevelManagerSpec extends Specification { when: 'method to register to the cache is called' objectUnderTest.registerCmHandles(cmHandleModelsToBeCreated) then: 'no notification sent' - 0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) + 0 * mockInventoryEventProducer.publishAvcEvent(*_) and: 'both cm handles are in the cache and are trusted' assert trustLevelPerCmHandleId.get('ch-1') == TrustLevel.COMPLETE assert trustLevelPerCmHandleId.get('ch-2') == TrustLevel.COMPLETE @@ -83,7 +83,7 @@ class TrustLevelManagerSpec extends Specification { when: 'method to register to the cache is called' objectUnderTest.registerCmHandles(cmHandleModelsToBeCreated) then: 'notification is sent' - 1 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) + 1 * mockInventoryEventProducer.publishAvcEvent(*_) } def 'Dmi trust level updated'() { @@ -94,7 +94,7 @@ class TrustLevelManagerSpec extends Specification { when: 'the update is handled' objectUnderTest.updateDmi('my-dmi', ['ch-1'], TrustLevel.NONE) then: 'notification is sent' - 1 * mockAttributeValueChangeEventPublisher.publishAvcEvent('ch-1', 'trustLevel', 'COMPLETE', 'NONE') + 1 * mockInventoryEventProducer.publishAvcEvent('ch-1', 'trustLevel', 'COMPLETE', 'NONE') and: 'the dmi in the cache is not trusted' assert trustLevelPerDmiPlugin.get('my-dmi') == TrustLevel.NONE } @@ -107,7 +107,7 @@ class TrustLevelManagerSpec extends Specification { when: 'the update is handled' objectUnderTest.updateDmi('my-dmi', ['ch-1'], TrustLevel.COMPLETE) then: 'no notification is sent' - 0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) + 0 * mockInventoryEventProducer.publishAvcEvent(*_) and: 'the dmi in the cache is trusted' assert trustLevelPerDmiPlugin.get('my-dmi') == TrustLevel.COMPLETE } @@ -124,7 +124,7 @@ class TrustLevelManagerSpec extends Specification { then: 'the cm handle in the cache is trusted' assert trustLevelPerCmHandleId.get('ch-1', TrustLevel.COMPLETE) and: 'notification is sent' - 1 * mockAttributeValueChangeEventPublisher.publishAvcEvent('ch-1', 'trustLevel', 'NONE', 'COMPLETE') + 1 * mockInventoryEventProducer.publishAvcEvent('ch-1', 'trustLevel', 'NONE', 'COMPLETE') } def 'CmHandle trust level updated with same value'() { @@ -139,7 +139,7 @@ class TrustLevelManagerSpec extends Specification { then: 'the cm handle in the cache is not trusted' assert trustLevelPerCmHandleId.get('ch-1', TrustLevel.NONE) and: 'no notification is sent' - 0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) + 0 * mockInventoryEventProducer.publishAvcEvent(*_) } def 'Dmi trust level restored to complete with non trusted CmHandle'() { @@ -152,7 +152,7 @@ class TrustLevelManagerSpec extends Specification { then: 'the cm handle in the cache is still NONE' assert trustLevelPerCmHandleId.get('ch-1') == TrustLevel.NONE and: 'no notification is sent' - 0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) + 0 * mockInventoryEventProducer.publishAvcEvent(*_) } def 'Apply effective trust level among CmHandle and dmi plugin'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/events/CmAvcEventPublisherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/events/InventoryEventProducerSpec.groovy index 051f5df4cf..1aa7aab0f3 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/events/CmAvcEventPublisherSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/events/InventoryEventProducerSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2023-2024 Nordix Foundation. + * Copyright (c) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,10 +30,10 @@ import org.onap.cps.utils.JsonObjectMapper import org.springframework.test.context.ContextConfiguration @ContextConfiguration(classes = [CpsApplicationContext, ObjectMapper, JsonObjectMapper]) -class CmAvcEventPublisherSpec extends MessagingBaseSpec { +class InventoryEventProducerSpec extends MessagingBaseSpec { def mockEventsPublisher = Mock(EventsPublisher<CloudEvent>) - def objectUnderTest = new CmAvcEventPublisher(mockEventsPublisher) + def objectUnderTest = new InventoryEventProducer(mockEventsPublisher) def 'Publish an attribute value change event'() { given: 'the event key' diff --git a/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java index 575f9d7d3b..472da34833 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 Nordix Foundation + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2020-2022 Bell Canada. * Modifications Copyright (C) 2022-2023 TechMahindra Ltd. @@ -24,7 +24,6 @@ package org.onap.cps.ri; import static org.onap.cps.api.CpsQueryService.NO_LIMIT; -import static org.onap.cps.api.parameters.FetchDescendantsOption.OMIT_DESCENDANTS; import static org.onap.cps.api.parameters.PaginationOption.NO_PAGINATION; import com.google.common.collect.ImmutableSet; @@ -39,7 +38,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; @@ -249,17 +247,9 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService throw new IllegalArgumentException( "Only Cps Path Queries with attribute-axis are supported by queryDataLeaf"); } - - final String attributeName = cpsPathQuery.getAttributeAxisAttributeName(); - final Collection<DataNode> dataNodes = queryDataNodes(dataspaceName, anchorName, cpsPath, - OMIT_DESCENDANTS, queryResultLimit); - return dataNodes.stream() - .map(dataNode -> { - final Object attributeValue = dataNode.getLeaves().get(attributeName); - return targetClass.isInstance(attributeValue) ? targetClass.cast(attributeValue) : null; - }) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + return fragmentRepository.findAttributeValuesByAnchorAndCpsPath(anchorEntity, cpsPathQuery, + cpsPathQuery.getAttributeAxisAttributeName(), queryResultLimit, targetClass); } @Override diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepository.java index 87d7697df4..75853319da 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepository.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023 Nordix Foundation. + * Copyright (C) 2023 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepositoryImpl.java index 6b95213a13..decd864611 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepositoryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepositoryImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023 Nordix Foundation. + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,8 +52,7 @@ public class FragmentPrefetchRepositoryImpl implements FragmentPrefetchRepositor return proxiedFragmentEntities; } - final List<Long> fragmentEntityIds = proxiedFragmentEntities.stream() - .map(FragmentEntity::getId).collect(Collectors.toList()); + final List<Long> fragmentEntityIds = proxiedFragmentEntities.stream().map(FragmentEntity::getId).toList(); final Map<Long, AnchorEntity> anchorEntityPerId = proxiedFragmentEntities.stream() .map(FragmentEntity::getAnchor) @@ -69,19 +68,20 @@ public class FragmentPrefetchRepositoryImpl implements FragmentPrefetchRepositor final Collection<Long> fragmentEntityIds, final Map<Long, AnchorEntity> anchorEntityPerId, final int maxDepth) { - final String sql - = "WITH RECURSIVE parent_search AS (" - + " SELECT id, 0 AS depth " - + " FROM fragment " - + " WHERE id = ANY (?) " - + " UNION " - + " SELECT child.id, depth + 1 " - + " FROM fragment child INNER JOIN parent_search parent ON child.parent_id = parent.id" - + " WHERE depth < ?" - + ") " - + "SELECT fragment.id, anchor_id AS anchorId, xpath, parent_id AS parentId, " - + " CAST(attributes AS TEXT) AS attributes " - + "FROM fragment INNER JOIN parent_search ON fragment.id = parent_search.id"; + final String sql = """ + WITH RECURSIVE fragment_hierarchy AS ( + SELECT id, anchor_id, xpath, parent_id, attributes, 0 AS depth + FROM fragment + WHERE id = ANY(?) + UNION + SELECT child.id, child.anchor_id, child.xpath, child.parent_id, child.attributes, depth + 1 + FROM fragment child + INNER JOIN fragment_hierarchy parent ON child.parent_id = parent.id + WHERE depth < ? + ) + SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId, attributes + FROM fragment_hierarchy; + """; final PreparedStatementSetter preparedStatementSetter = preparedStatement -> { final Connection connection = preparedStatement.getConnection(); diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java index 0f17b6f931..3b88748545 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2025 Nordix Foundation + * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -61,17 +61,23 @@ public class FragmentQueryBuilder { * @return a executable query object */ public Query getQueryForAnchorAndCpsPath(final AnchorEntity anchorEntity, - final CpsPathQuery cpsPathQuery, - final int queryResultLimit) { + final CpsPathQuery cpsPathQuery, + final int queryResultLimit) { final StringBuilder sqlStringBuilder = new StringBuilder(); final Map<String, Object> queryParameters = new HashMap<>(); addSearchPrefix(cpsPathQuery, sqlStringBuilder); addWhereClauseForAnchor(anchorEntity, sqlStringBuilder, queryParameters); + if (cpsPathQuery.hasAttributeAxis() && !cpsPathQuery.hasAncestorAxis()) { + sqlStringBuilder.append(" AND jsonb_exists(fragment.attributes, :attributeName)"); + } addNodeSearchConditions(cpsPathQuery, sqlStringBuilder, queryParameters, false); addSearchSuffix(cpsPathQuery, sqlStringBuilder, queryParameters); addLimitClause(sqlStringBuilder, queryParameters, queryResultLimit); - + if (cpsPathQuery.hasAttributeAxis()) { + queryParameters.put("attributeName", cpsPathQuery.getAttributeAxisAttributeName()); + return getQuery(sqlStringBuilder.toString(), queryParameters, String.class); + } return getQuery(sqlStringBuilder.toString(), queryParameters, FragmentEntity.class); } @@ -312,7 +318,10 @@ public class FragmentQueryBuilder { WHERE parentFragment.id IN ( SELECT parent_id FROM fragment"""); } else { - sqlStringBuilder.append("SELECT fragment.* FROM fragment"); + final String fieldsToSelect = cpsPathQuery.hasAttributeAxis() + ? "DISTINCT (attributes -> :attributeName)" + : "fragment.*"; + sqlStringBuilder.append("SELECT ").append(fieldsToSelect).append(" FROM fragment"); } } @@ -327,9 +336,14 @@ public class FragmentQueryBuilder { FROM fragment JOIN ancestors ON ancestors.parent_id = fragment.id ) - SELECT * FROM ancestors - WHERE"""); - + """); + if (cpsPathQuery.hasAttributeAxis()) { + sqlStringBuilder.append(""" + SELECT DISTINCT (attributes -> :attributeName) FROM ancestors WHERE + jsonb_exists(ancestors.attributes, :attributeName) AND"""); + } else { + sqlStringBuilder.append("SELECT * FROM ancestors WHERE"); + } final String ancestorPath = DESCENDANT_PATH + cpsPathQuery.getAncestorSchemaNodeIdentifier(); final CpsPathQuery ancestorCpsPathQuery = CpsPathUtil.getCpsPathQuery(ancestorPath); addAncestorNodeSearchCondition(ancestorCpsPathQuery, sqlStringBuilder, queryParameters); diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java index 50c7494b29..a24b280bf8 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 Nordix Foundation. + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ package org.onap.cps.ri.repository; import java.util.List; +import java.util.Set; import org.onap.cps.api.parameters.PaginationOption; import org.onap.cps.cpspath.parser.CpsPathQuery; import org.onap.cps.ri.models.AnchorEntity; @@ -33,6 +34,9 @@ public interface FragmentRepositoryCpsPathQuery { List<FragmentEntity> findByAnchorAndCpsPath(AnchorEntity anchorEntity, CpsPathQuery cpsPathQuery, int queryResultLimit); + <T> Set<T> findAttributeValuesByAnchorAndCpsPath(AnchorEntity anchorEntity, CpsPathQuery cpsPathQuery, + String attributeName, int queryResultLimit, Class<T> targetClass); + List<FragmentEntity> findByDataspaceAndCpsPath(DataspaceEntity dataspaceEntity, CpsPathQuery cpsPathQuery, List<Long> anchorIds); diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java index 80fbe9b6cd..cc8055d3c1 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation. + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,8 @@ package org.onap.cps.ri.repository; import jakarta.persistence.Query; import jakarta.transaction.Transactional; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.parameters.PaginationOption; @@ -31,20 +33,22 @@ import org.onap.cps.cpspath.parser.CpsPathQuery; import org.onap.cps.ri.models.AnchorEntity; import org.onap.cps.ri.models.DataspaceEntity; import org.onap.cps.ri.models.FragmentEntity; +import org.onap.cps.utils.JsonObjectMapper; @RequiredArgsConstructor @Slf4j public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCpsPathQuery { private final FragmentQueryBuilder fragmentQueryBuilder; + private final JsonObjectMapper jsonObjectMapper; @Override @Transactional public List<FragmentEntity> findByAnchorAndCpsPath(final AnchorEntity anchorEntity, final CpsPathQuery cpsPathQuery, final int queryResultLimit) { - final Query query = fragmentQueryBuilder - .getQueryForAnchorAndCpsPath(anchorEntity, cpsPathQuery, queryResultLimit); + final Query query = fragmentQueryBuilder.getQueryForAnchorAndCpsPath(anchorEntity, cpsPathQuery, + queryResultLimit); final List<FragmentEntity> fragmentEntities = query.getResultList(); log.debug("Fetched {} fragment entities by anchor and cps path.", fragmentEntities.size()); if (queryResultLimit > 0) { @@ -55,6 +59,21 @@ public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCps @Override @Transactional + public <T> Set<T> findAttributeValuesByAnchorAndCpsPath(final AnchorEntity anchorEntity, + final CpsPathQuery cpsPathQuery, + final String attributeName, + final int queryResultLimit, + final Class<T> targetClass) { + final Query query = fragmentQueryBuilder.getQueryForAnchorAndCpsPath(anchorEntity, cpsPathQuery, + queryResultLimit); + final List<String> jsonResultList = query.getResultList(); + return jsonResultList.stream() + .map(jsonValue -> jsonObjectMapper.convertJsonString(jsonValue, targetClass)) + .collect(Collectors.toSet()); + } + + @Override + @Transactional public List<FragmentEntity> findByDataspaceAndCpsPath(final DataspaceEntity dataspaceEntity, final CpsPathQuery cpsPathQuery, final List<Long> anchorIds) { final Query query = fragmentQueryBuilder.getQueryForDataspaceAndCpsPath( diff --git a/cps-service/src/main/java/org/onap/cps/events/CpsDataUpdateEventsService.java b/cps-service/src/main/java/org/onap/cps/events/CpsDataUpdateEventsProducer.java index 3bcc1923a4..3061fd2022 100644 --- a/cps-service/src/main/java/org/onap/cps/events/CpsDataUpdateEventsService.java +++ b/cps-service/src/main/java/org/onap/cps/events/CpsDataUpdateEventsProducer.java @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2024-2025 TechMahindra Ltd. - * Copyright (C) 2024 Nordix Foundation. + * Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ import org.springframework.stereotype.Service; @Slf4j @Service @RequiredArgsConstructor -public class CpsDataUpdateEventsService { +public class CpsDataUpdateEventsProducer { private final EventsPublisher<CpsDataUpdatedEvent> eventsPublisher; diff --git a/cps-service/src/main/java/org/onap/cps/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/impl/CpsDataServiceImpl.java index a93bf9ac82..586941a561 100644 --- a/cps-service/src/main/java/org/onap/cps/impl/CpsDataServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/impl/CpsDataServiceImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 Nordix Foundation + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2020-2022 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2022-2025 TechMahindra Ltd. @@ -48,7 +48,7 @@ import org.onap.cps.api.model.DataNode; import org.onap.cps.api.model.DeltaReport; import org.onap.cps.api.parameters.FetchDescendantsOption; import org.onap.cps.cpspath.parser.CpsPathUtil; -import org.onap.cps.events.CpsDataUpdateEventsService; +import org.onap.cps.events.CpsDataUpdateEventsProducer; import org.onap.cps.events.model.Data.Operation; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.utils.ContentType; @@ -66,7 +66,7 @@ public class CpsDataServiceImpl implements CpsDataService { private static final long DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS = 300L; private final CpsDataPersistenceService cpsDataPersistenceService; - private final CpsDataUpdateEventsService cpsDataUpdateEventsService; + private final CpsDataUpdateEventsProducer cpsDataUpdateEventsProducer; private final CpsAnchorService cpsAnchorService; private final DataNodeFactory dataNodeFactory; @@ -396,7 +396,7 @@ public class CpsDataServiceImpl implements CpsDataService { final Operation operation, final OffsetDateTime observedTimestamp) { try { - cpsDataUpdateEventsService.publishCpsDataUpdateEvent(anchor, xpath, operation, observedTimestamp); + cpsDataUpdateEventsProducer.publishCpsDataUpdateEvent(anchor, xpath, operation, observedTimestamp); } catch (final Exception exception) { log.error("Failed to send message to notification service", exception); } diff --git a/cps-service/src/main/java/org/onap/cps/impl/CpsDataspaceServiceImpl.java b/cps-service/src/main/java/org/onap/cps/impl/CpsDataspaceServiceImpl.java index 1a85147b64..ac55b81bdc 100644 --- a/cps-service/src/main/java/org/onap/cps/impl/CpsDataspaceServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/impl/CpsDataspaceServiceImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2020-2023 Nordix Foundation + * Copyright (C) 2020-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2020-2022 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2022 TechMahindra Ltd. diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java index b319929e47..138fc34ca1 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2020-2025 Nordix Foundation. + * Copyright (C) 2020-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2022 Bell Canada * Modifications Copyright (C) 2022-2023 TechMahindra Ltd. diff --git a/cps-service/src/test/groovy/org/onap/cps/events/CpsDataUpdateEventsServiceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/events/CpsDataUpdateEventsProducerSpec.groovy index e5160a0be7..641e399622 100644 --- a/cps-service/src/test/groovy/org/onap/cps/events/CpsDataUpdateEventsServiceSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/events/CpsDataUpdateEventsProducerSpec.groovy @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2024-2025 TechMahindra Ltd. - * Copyright (C) 2024 Nordix Foundation. + * Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,12 +39,12 @@ import static org.onap.cps.events.model.Data.Operation.DELETE import static org.onap.cps.events.model.Data.Operation.UPDATE @ContextConfiguration(classes = [ObjectMapper, JsonObjectMapper]) -class CpsDataUpdateEventsServiceSpec extends Specification { +class CpsDataUpdateEventsProducerSpec extends Specification { def mockEventsPublisher = Mock(EventsPublisher) def objectMapper = new ObjectMapper(); def mockCpsNotificationService = Mock(CpsNotificationService) - def objectUnderTest = new CpsDataUpdateEventsService(mockEventsPublisher, mockCpsNotificationService) + def objectUnderTest = new CpsDataUpdateEventsProducer(mockEventsPublisher, mockCpsNotificationService) def setup() { mockCpsNotificationService.isNotificationEnabled('dataspace01', 'anchor01') >> true diff --git a/cps-service/src/test/groovy/org/onap/cps/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/impl/CpsDataServiceImplSpec.groovy index 967bcc0aa0..a4bfd569c5 100644 --- a/cps-service/src/test/groovy/org/onap/cps/impl/CpsDataServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/impl/CpsDataServiceImplSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 Nordix Foundation + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada. * Modifications Copyright (C) 2022-2025 TechMahindra Ltd. @@ -37,7 +37,7 @@ import org.onap.cps.api.exceptions.SessionManagerException import org.onap.cps.api.exceptions.SessionTimeoutException import org.onap.cps.api.model.Anchor import org.onap.cps.api.parameters.FetchDescendantsOption -import org.onap.cps.events.CpsDataUpdateEventsService +import org.onap.cps.events.CpsDataUpdateEventsProducer import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.utils.ContentType import org.onap.cps.utils.CpsValidator @@ -66,13 +66,13 @@ class CpsDataServiceImplSpec extends Specification { def mockTimedYangTextSchemaSourceSetBuilder = Mock(TimedYangTextSchemaSourceSetBuilder) def yangParser = new YangParser(new YangParserHelper(), mockYangTextSchemaSourceSetCache, mockTimedYangTextSchemaSourceSetBuilder) def mockCpsDeltaService = Mock(CpsDeltaService); - def mockDataUpdateEventsService = Mock(CpsDataUpdateEventsService) + def mockCpsDataUpdateEventsProducer = Mock(CpsDataUpdateEventsProducer) def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) def mockPrefixResolver = Mock(PrefixResolver) def dataMapper = new DataMapper(mockCpsAnchorService, mockPrefixResolver) def dataNodeFactory = new DataNodeFactoryImpl(yangParser) - def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockDataUpdateEventsService, mockCpsAnchorService, + def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsDataUpdateEventsProducer, mockCpsAnchorService, dataNodeFactory, mockCpsValidator, yangParser, mockCpsDeltaService, dataMapper, jsonObjectMapper) def logger = (Logger) LoggerFactory.getLogger(objectUnderTest.class) @@ -577,8 +577,8 @@ class CpsDataServiceImplSpec extends Specification { and: 'the persistence service method is invoked with the correct parameters' 1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, _ as Collection<String>) and: 'a data update event is sent for each anchor' - 1 * mockDataUpdateEventsService.publishCpsDataUpdateEvent(anchor1, '/', DELETE, observedTimestamp) - 1 * mockDataUpdateEventsService.publishCpsDataUpdateEvent(anchor2, '/', DELETE, observedTimestamp) + 1 * mockCpsDataUpdateEventsProducer.publishCpsDataUpdateEvent(anchor1, '/', DELETE, observedTimestamp) + 1 * mockCpsDataUpdateEventsProducer.publishCpsDataUpdateEvent(anchor2, '/', DELETE, observedTimestamp) } def "Validating #scenario when dry run is enabled."() { @@ -643,7 +643,7 @@ class CpsDataServiceImplSpec extends Specification { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') when: 'publisher set to throw an exception' - mockDataUpdateEventsService.publishCpsDataUpdateEvent(_, _, _, _) >> { throw new Exception("publishing failed")} + mockCpsDataUpdateEventsProducer.publishCpsDataUpdateEvent(_, _, _, _) >> { throw new Exception("publishing failed")} and: 'an update event is performed' objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, '/', '{"test-tree": {"branch": []}}', observedTimestamp, ContentType.JSON) then: 'the exception is not bubbled up' diff --git a/cps-service/src/test/groovy/org/onap/cps/impl/E2ENetworkSliceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/impl/E2ENetworkSliceSpec.groovy index 893cce6687..c9c1991c53 100755 --- a/cps-service/src/test/groovy/org/onap/cps/impl/E2ENetworkSliceSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/impl/E2ENetworkSliceSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 Nordix Foundation. + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2021-2022 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2022-2025 TechMahindra Ltd. @@ -28,7 +28,7 @@ import org.onap.cps.TestUtils import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsDeltaService import org.onap.cps.api.model.Anchor -import org.onap.cps.events.CpsDataUpdateEventsService +import org.onap.cps.events.CpsDataUpdateEventsProducer import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.utils.ContentType @@ -56,9 +56,9 @@ class E2ENetworkSliceSpec extends Specification { def cpsModuleServiceImpl = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAnchorService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder) - def mockDataUpdateEventsService = Mock(CpsDataUpdateEventsService) + def mockCpsDataUpdateEventsProducer = Mock(CpsDataUpdateEventsProducer) def dataNodeFactory = new DataNodeFactoryImpl(yangParser) - def cpsDataServiceImpl = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockDataUpdateEventsService, mockCpsAnchorService, dataNodeFactory, mockCpsValidator, yangParser, mockCpsDeltaService, dataMapper, jsonObjectMapper) + def cpsDataServiceImpl = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsDataUpdateEventsProducer, mockCpsAnchorService, dataNodeFactory, mockCpsValidator, yangParser, mockCpsDeltaService, dataMapper, jsonObjectMapper) def dataspaceName = 'someDataspace' def anchorName = 'someAnchor' def schemaSetName = 'someSchemaSet' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy index aa80e7f6e8..212686e917 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023-2025 Nordix Foundation + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2023-2025 TechMahindra Ltd * ================================================================================ * Licensed under the Apache License, Version 2.0 (the 'License'); @@ -66,6 +66,7 @@ class QueryServiceIntegrationSpec extends FunctionalSpecBase { 'all books' | '//books/@title' || 19 'all books in a category' | '/bookstore/categories[@code=5]/books/@title' || 10 'non-existing path' | '/non-existing/@title' || 0 + 'non-existing attribute' | '//books/@non-existing' || 0 } def 'Query data leaf with type #leafType using CPS path.'() { @@ -78,7 +79,7 @@ class QueryServiceIntegrationSpec extends FunctionalSpecBase { where: leafName | leafType || expectedResults 'lang' | String.class || ['English'] - 'price' | Number.class || [13, 20] + 'price' | Integer.class || [13, 20] 'editions' | List.class || [[1988, 2000], [2006]] } @@ -91,6 +92,15 @@ class QueryServiceIntegrationSpec extends FunctionalSpecBase { assert result == ['Children', 'Comedy'] as Set } + def 'Attempt to query data leaf without specifying leaf name gives an error.'() { + given: 'a cps path without an attribute axis' + def cpsPathWithoutAttributeAxis = '//books' + when: 'query data leaf is called without attribute axis in cps path' + objectUnderTest.queryDataLeaf(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, cpsPathWithoutAttributeAxis, String.class) + then: 'illegal argument exception is thrown' + thrown(IllegalArgumentException) + } + def 'Cps Path query using comparative and boolean operators.'() { given: 'a cps path query in the discount category' def cpsPath = "/bookstore/categories[@code='5']/books" + leafCondition diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy index 70639c3c70..8c429b3a30 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023-2025 Nordix Foundation + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -116,9 +116,9 @@ class QueryPerfTest extends CpsPerfTestBase { recordAndAssertResourceUsage("Query data leaf ${scenario}", durationLimit, durationInSeconds, memoryLimit, resourceMeter.getTotalMemoryUsageInMB()) where: 'the following parameters are used' scenario | cpsPath || durationLimit | memoryLimit | expectedNumberOfValues - 'unique leaf value' | '/openroadm-devices/openroadm-device/@device-id' || 0.10 | 8 | OPENROADM_DEVICES_PER_ANCHOR - 'common leaf value' | '/openroadm-devices/openroadm-device/@ne-state' || 0.05 | 1 | 1 - 'non-existing data leaf' | '/openroadm-devices/openroadm-device/@non-existing' || 0.05 | 1 | 0 + 'unique leaf value' | '/openroadm-devices/openroadm-device/@device-id' || 0.05 | 0.1 | OPENROADM_DEVICES_PER_ANCHOR + 'common leaf value' | '/openroadm-devices/openroadm-device/@ne-state' || 0.02 | 0.1 | 1 + 'non-existing data leaf' | '/openroadm-devices/openroadm-device/@non-existing' || 0.01 | 0.1 | 0 } } |