summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cps-application/src/main/resources/application.yml395
-rw-r--r--cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java101
-rw-r--r--cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java145
-rw-r--r--cps-ncmp-rest/docs/openapi/components.yaml14
-rwxr-xr-xcps-ncmp-rest/docs/openapi/ncmp.yml39
-rwxr-xr-xcps-ncmp-rest/docs/openapi/openapi.yml5
-rwxr-xr-xcps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java78
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java70
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java56
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java53
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java51
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java50
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java116
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java41
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java58
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java63
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy124
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy19
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java55
-rwxr-xr-xcps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java42
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java12
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java52
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfig.java51
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ResponseTimeoutTask.java51
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumer.java82
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java69
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java47
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java34
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java186
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java9
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java16
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java19
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java43
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java64
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java68
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java8
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/SubscriptionEventResponse.java37
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/SubscriptionModelLoader.java33
-rw-r--r--cps-ncmp-service/src/main/resources/model/subscription.yang2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy78
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy9
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy41
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfigSpec.groovy75
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumerSpec.groovy79
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/SubscriptionEventMapperSpec.groovy19
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarderSpec.groovy38
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy41
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy18
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy11
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy13
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy8
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/SubscriptionModelLoaderSpec.groovy36
-rw-r--r--cps-ncmp-service/src/test/resources/application.yml9
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java4
-rw-r--r--cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy19
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java15
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java2
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java50
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java49
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java72
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java65
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java27
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java15
-rwxr-xr-xcps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy53
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy18
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy19
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy9
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy2
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy6
-rw-r--r--cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java32
-rw-r--r--cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java85
-rw-r--r--cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java9
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy28
-rw-r--r--cps-service/src/test/resources/application.yml7
-rw-r--r--dmi-plugin-stub/mappings/batchCmHandles.json2
-rw-r--r--docs/cps-path.rst7
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy52
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimits.groovy13
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimits.groovy21
79 files changed, 2316 insertions, 1298 deletions
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index 0007bc824..1eb1c11f2 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -1,190 +1,205 @@
-# ============LICENSE_START=======================================================
-# Copyright (C) 2021 Pantheon.tech
-# Modifications Copyright (C) 2021-2022 Bell Canada
-# Modifications Copyright (C) 2021-2023 Nordix Foundation
-# ================================================================================
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# SPDX-License-Identifier: Apache-2.0
-# ============LICENSE_END=========================================================
-
-server:
- port: 8080
-
-rest:
- api:
- cps-base-path: /cps/api
- ncmp-base-path: /ncmp
- ncmp-inventory-base-path: /ncmpInventory
-
-spring:
- main:
- banner-mode: "off"
- application:
- name: "cps-application"
- jpa:
- show-sql: false
- ddl-auto: create
- open-in-view: false
- properties:
- hibernate:
- enable_lazy_load_no_trans: true
- dialect: org.hibernate.dialect.PostgreSQLDialect
-
- datasource:
- url: jdbc:postgresql://${DB_HOST}:${DB_PORT:5432}/cpsdb
- username: ${DB_USERNAME}
- password: ${DB_PASSWORD}
- driverClassName: org.postgresql.Driver
- hikari:
- minimumIdle: 5
- maximumPoolSize: 80
- idleTimeout: 60000
- connectionTimeout: 120000
- leakDetectionThreshold: 30000
- pool-name: CpsDatabasePool
-
- cache:
- type: caffeine
- cache-names: yangSchema
- caffeine:
- spec: maximumSize=10000,expireAfterAccess=10m
-
- liquibase:
- change-log: classpath:changelog/changelog-master.yaml
- labels: ${LIQUIBASE_LABELS}
-
- servlet:
- multipart:
- enabled: true
- max-file-size: 100MB
- max-request-size: 100MB
-
- kafka:
- bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVER:localhost:9092}
- security:
- protocol: PLAINTEXT
- producer:
- value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
- client-id: cps-core
- consumer:
- group-id: ${NCMP_CONSUMER_GROUP_ID:ncmp-group}
- key-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
- value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
- properties:
- spring.deserializer.key.delegate.class: org.apache.kafka.common.serialization.StringDeserializer
- spring.deserializer.value.delegate.class: org.springframework.kafka.support.serializer.JsonDeserializer
- spring.json.use.type.headers: false
-
- jackson:
- default-property-inclusion: NON_NULL
- serialization:
- FAIL_ON_EMPTY_BEANS: false
- sql:
- init:
- mode: ALWAYS
-app:
- ncmp:
- async-m2m:
- topic: ${NCMP_ASYNC_M2M_TOPIC:ncmp-async-m2m}
- avc:
- subscription-topic: ${NCMP_CM_AVC_SUBSCRIPTION:cm-avc-subscription}
- cm-events-topic: ${NCMP_CM_EVENTS_TOPIC:cm-events}
- lcm:
- events:
- topic: ${LCM_EVENTS_TOPIC:ncmp-events}
- dmi:
- cm-events:
- topic: ${DMI_CM_EVENTS_TOPIC:dmi-cm-events}
-
-
-notification:
- enabled: true
- data-updated:
- topic: ${CPS_CHANGE_EVENT_TOPIC:cps.data-updated-events}
- filters:
- enabled-dataspaces: ${NOTIFICATION_DATASPACE_FILTER_PATTERNS:""}
- async:
- executor:
- core-pool-size: 2
- max-pool-size: 10
- queue-capacity: 500
- wait-for-tasks-to-complete-on-shutdown: true
- thread-name-prefix: Async-
- time-out-value-in-ms: 2000
-
-springdoc:
- swagger-ui:
- disable-swagger-default-url: true
- urlsPrimaryName: cps-core
- urls:
- - name: cps-core
- url: /api-docs/cps-core/openapi.yaml
- - name: cps-ncmp
- url: /api-docs/cps-ncmp/openapi.yaml
- - name: cps-ncmp-inventory
- url: /api-docs/cps-ncmp/openapi-inventory.yaml
-
-
-security:
- # comma-separated uri patterns which do not require authorization
- permit-uri: /manage/**,/swagger-ui.html,/swagger-ui/**,/swagger-resources/**,/api-docs/**
- auth:
- username: ${CPS_USERNAME}
- password: ${CPS_PASSWORD}
-
-# Actuator
-management:
- server:
- port: 8081
- endpoints:
- web:
- base-path: /manage
- exposure:
- include: info,health,loggers,prometheus
- endpoint:
- health:
- show-details: always
- # kubernetes probes: liveness and readiness
- probes:
- enabled: true
-
-logging:
- format: json
- level:
- org:
- springframework: INFO
- onap:
- cps: INFO
-ncmp:
- dmi:
- auth:
- username: ${DMI_USERNAME}
- password: ${DMI_PASSWORD}
- api:
- base-path: dmi
-
- timers:
- advised-modules-sync:
- sleep-time-ms: 5000
- locked-modules-sync:
- sleep-time-ms: 300000
- cm-handle-data-sync:
- sleep-time-ms: 30000
-
- modules-sync-watchdog:
- async-executor:
- parallelism-level: 10
-
- model-loader:
- subscription: false \ No newline at end of file
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 Pantheon.tech
+# Modifications Copyright (C) 2021-2022 Bell Canada
+# Modifications Copyright (C) 2021-2023 Nordix Foundation
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+
+server:
+ port: 8080
+
+rest:
+ api:
+ cps-base-path: /cps/api
+ ncmp-base-path: /ncmp
+ ncmp-inventory-base-path: /ncmpInventory
+
+spring:
+ main:
+ banner-mode: "off"
+ application:
+ name: "cps-application"
+ jpa:
+ show-sql: false
+ ddl-auto: create
+ open-in-view: false
+ properties:
+ hibernate:
+ enable_lazy_load_no_trans: true
+ dialect: org.hibernate.dialect.PostgreSQLDialect
+
+ datasource:
+ url: jdbc:postgresql://${DB_HOST}:${DB_PORT:5432}/cpsdb
+ username: ${DB_USERNAME}
+ password: ${DB_PASSWORD}
+ driverClassName: org.postgresql.Driver
+ hikari:
+ minimumIdle: 5
+ maximumPoolSize: 80
+ idleTimeout: 60000
+ connectionTimeout: 120000
+ leakDetectionThreshold: 30000
+ pool-name: CpsDatabasePool
+
+ cache:
+ type: caffeine
+ cache-names: yangSchema
+ caffeine:
+ spec: maximumSize=10000,expireAfterAccess=10m
+
+ liquibase:
+ change-log: classpath:changelog/changelog-master.yaml
+ labels: ${LIQUIBASE_LABELS}
+
+ servlet:
+ multipart:
+ enabled: true
+ max-file-size: 100MB
+ max-request-size: 100MB
+
+ kafka:
+ bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVER:localhost:9092}
+ security:
+ protocol: PLAINTEXT
+ producer:
+ value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
+ client-id: cps-core
+ consumer:
+ group-id: ${NCMP_CONSUMER_GROUP_ID:ncmp-group}
+ key-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
+ value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
+ properties:
+ spring.deserializer.key.delegate.class: org.apache.kafka.common.serialization.StringDeserializer
+ spring.deserializer.value.delegate.class: org.springframework.kafka.support.serializer.JsonDeserializer
+ spring.json.use.type.headers: false
+
+ jackson:
+ default-property-inclusion: NON_NULL
+ serialization:
+ FAIL_ON_EMPTY_BEANS: false
+ sql:
+ init:
+ mode: ALWAYS
+app:
+ ncmp:
+ async-m2m:
+ topic: ${NCMP_ASYNC_M2M_TOPIC:ncmp-async-m2m}
+ avc:
+ subscription-topic: ${NCMP_CM_AVC_SUBSCRIPTION:cm-avc-subscription}
+ subscription-forward-topic: ${NCMP_FORWARD_CM_AVC_SUBSCRIPTION:ncmp-dmi-cm-avc-subscription}
+ subscription-response-topic: ${NCMP_RESPONSE_CM_AVC_SUBSCRIPTION:dmi-ncmp-cm-avc-subscription}
+ subscription-outcome-topic: ${NCMP_OUTCOME_CM_AVC_SUBSCRIPTION:cm-avc-subscription-response}
+ cm-events-topic: ${NCMP_CM_EVENTS_TOPIC:cm-events}
+ lcm:
+ events:
+ topic: ${LCM_EVENTS_TOPIC:ncmp-events}
+ dmi:
+ cm-events:
+ topic: ${DMI_CM_EVENTS_TOPIC:dmi-cm-events}
+
+
+notification:
+ enabled: true
+ data-updated:
+ topic: ${CPS_CHANGE_EVENT_TOPIC:cps.data-updated-events}
+ filters:
+ enabled-dataspaces: ${NOTIFICATION_DATASPACE_FILTER_PATTERNS:""}
+ async:
+ executor:
+ core-pool-size: 2
+ max-pool-size: 10
+ queue-capacity: 500
+ wait-for-tasks-to-complete-on-shutdown: true
+ thread-name-prefix: Async-
+ time-out-value-in-ms: 2000
+
+springdoc:
+ swagger-ui:
+ disable-swagger-default-url: true
+ urlsPrimaryName: cps-core
+ urls:
+ - name: cps-core
+ url: /api-docs/cps-core/openapi.yaml
+ - name: cps-ncmp
+ url: /api-docs/cps-ncmp/openapi.yaml
+ - name: cps-ncmp-inventory
+ url: /api-docs/cps-ncmp/openapi-inventory.yaml
+
+
+security:
+ # comma-separated uri patterns which do not require authorization
+ permit-uri: /manage/**,/swagger-ui.html,/swagger-ui/**,/swagger-resources/**,/api-docs/**
+ auth:
+ username: ${CPS_USERNAME}
+ password: ${CPS_PASSWORD}
+
+# Actuator
+management:
+ server:
+ port: 8081
+ endpoints:
+ web:
+ base-path: /manage
+ exposure:
+ include: info,health,loggers,prometheus
+ endpoint:
+ health:
+ show-details: always
+ # kubernetes probes: liveness and readiness
+ probes:
+ enabled: true
+
+logging:
+ format: json
+ level:
+ org:
+ springframework: INFO
+ onap:
+ cps: INFO
+ncmp:
+ dmi:
+ auth:
+ username: ${DMI_USERNAME}
+ password: ${DMI_PASSWORD}
+ api:
+ base-path: dmi
+
+ timers:
+ advised-modules-sync:
+ sleep-time-ms: 5000
+ locked-modules-sync:
+ sleep-time-ms: 300000
+ cm-handle-data-sync:
+ sleep-time-ms: 30000
+ subscription-forwarding:
+ dmi-response-timeout-ms: 30000
+ model-loader:
+ retry-time-ms: 1000
+
+ modules-sync-watchdog:
+ async-executor:
+ parallelism-level: 10
+
+ model-loader:
+ subscription: false
+ maximum-attempt-count: 20
+
+# Custom Hazelcast Config.
+hazelcast:
+ mode:
+ kubernetes:
+ enabled: ${HAZELCAST_MODE_KUBERNETES_ENABLED:false}
+ service-name: ${CPS_NCMP_SERVICE_NAME:"cps-and-ncmp-service"} \ No newline at end of file
diff --git a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
index 3990dd15d..6d5ee8ce6 100644
--- a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
+++ b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
@@ -1,7 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2022 Bell Canada
- * Modifications Copyright (c) 2022 Nordix Foundation
+ * Modifications Copyright (c) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,14 +31,10 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType;
import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
-import org.onap.cps.ncmp.rest.model.RestModuleDefinition;
-import org.onap.cps.ncmp.rest.model.RestModuleReference;
import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
-import org.onap.cps.ncmp.rest.model.RestOutputCmHandleCompositeState;
-import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
+import org.onap.cps.ncmp.rest.stub.handlers.NetworkCmProxyApiStubDefaultImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
@@ -49,9 +45,7 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("${rest.api.ncmp-stub-base-path}")
-public class NetworkCmProxyStubController implements NetworkCmProxyApi {
-
- public static final String ASYNC_REQUEST_ID = "requestId";
+public class NetworkCmProxyStubController implements NetworkCmProxyApiStubDefaultImpl {
@Value("${stub.path}")
private String pathToResponseFiles;
@@ -87,23 +81,6 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
}
@Override
- public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String datastoreName,
- final String resourceIdentifier,
- final String cmHandleId,
- final Object body,
- final String contentType) {
- return new ResponseEntity<>(HttpStatus.CREATED);
- }
-
- @Override
- public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName,
- final String cmHandleId,
- final String resourceIdentifier,
- final String contentType) {
- return new ResponseEntity<>(HttpStatus.NO_CONTENT);
- }
-
- @Override
public ResponseEntity<List<RestOutputCmHandle>> searchCmHandles(
final CmHandleQueryParameters cmHandleQueryParameters) {
List<RestOutputCmHandle> restOutputCmHandles = null;
@@ -120,78 +97,12 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
return ResponseEntity.ok(restOutputCmHandles);
}
- @Override
- public ResponseEntity<Object> setDataSyncEnabledFlagForCmHandle(final String cmHandleId,
- final Boolean dataSyncEnabled) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<List<String>> searchCmHandleIds(
- final CmHandleQueryParameters cmHandleQueryParameters) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
- final String cmHandleId) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(final String cmHandle) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<List<RestModuleDefinition>> getModuleDefinitionsByCmHandleId(final String cmHandle) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandleId) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String datastoreName,
- final String resourceIdentifier,
- final String cmHandleId,
- final Object body,
- final String contentType) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<Object> queryResourceDataForCmHandle(final String datastoreName,
- final String cmHandle,
- final String cpsPath,
- final String options,
- final String topic,
- final Boolean includeDescendants) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleId) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String datastoreName,
- final String resourceIdentifier,
- final String cmHandleId,
- final Object body,
- final String contentType) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
private ResponseEntity<Map<String, Object>> populateAsyncResponse(final String topicParamInQuery) {
final Map<String, Object> responseData;
- if (topicParamInQuery != null) {
- responseData = getAsyncResponseData();
- } else {
+ if (topicParamInQuery == null) {
responseData = null;
+ } else {
+ responseData = getAsyncResponseData();
}
return ResponseEntity.ok().body(responseData);
}
diff --git a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java
new file mode 100644
index 000000000..6e28dbc44
--- /dev/null
+++ b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java
@@ -0,0 +1,145 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.stub.handlers;
+
+import java.util.List;
+import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
+import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
+import org.onap.cps.ncmp.rest.model.RestModuleDefinition;
+import org.onap.cps.ncmp.rest.model.RestModuleReference;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandleCompositeState;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+public interface NetworkCmProxyApiStubDefaultImpl extends NetworkCmProxyApi {
+
+ String ASYNC_REQUEST_ID = "requestId";
+
+ @Override
+ default ResponseEntity<Object> getResourceDataForCmHandle(final String datastoreName,
+ final String cmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final Boolean includeDescendants) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<Object> getResourceDataForCmHandleBatch(final String resourceIdentifier,
+ final String topicParamInQuery,
+ final String datastoreName,
+ final Object requestBody,
+ final String optionsParamInQuery,
+ final Boolean includeDescendants) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<List<RestOutputCmHandle>> searchCmHandles(
+ final CmHandleQueryParameters cmHandleQueryParameters) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<Void> createResourceDataRunningForCmHandle(final String datastoreName,
+ final String resourceIdentifier,
+ final String cmHandleId,
+ final Object requestBody,
+ final String contentType) {
+ return new ResponseEntity<>(HttpStatus.CREATED);
+ }
+
+ @Override
+ default ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName,
+ final String cmHandleId,
+ final String resourceIdentifier,
+ final String contentType) {
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+
+ @Override
+ default ResponseEntity<Object> setDataSyncEnabledFlagForCmHandle(final String cmHandleId,
+ final Boolean dataSyncEnabled) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<List<String>> searchCmHandleIds(final CmHandleQueryParameters cmHandleQueryParameters) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
+ final String cmHandleId) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(final String cmHandle) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<List<RestModuleDefinition>> getModuleDefinitionsByCmHandleId(final String cmHandle) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandleId) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String datastoreName,
+ final String resourceIdentifier,
+ final String cmHandleId,
+ final Object requestBody,
+ final String contentType) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<Object> queryResourceDataForCmHandle(final String datastoreName,
+ final String cmHandle,
+ final String cpsPath,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final Boolean includeDescendants) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleId) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ default ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String datastoreName,
+ final String resourceIdentifier,
+ final String cmHandleId,
+ final Object requestBody,
+ final String contentType) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+}
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml
index 6ca63c7ca..7fc1063a1 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -1,5 +1,5 @@
# ============LICENSE_START=======================================================
-# Copyright (C) 2021-2022 Nordix Foundation
+# Copyright (C) 2021-2023 Nordix Foundation
# Modifications Copyright (C) 2021 Pantheon.tech
# Modifications Copyright (C) 2022 Bell Canada
# ================================================================================
@@ -522,6 +522,18 @@ components:
sample 1:
value:
topic: my-topic-name
+ requiredTopicParamInQuery:
+ name: topic
+ in: query
+ description: mandatory topic parameter in query.
+ required: true
+ schema:
+ type: string
+ allowReserved: true
+ examples:
+ sample 1:
+ value:
+ topic: my-topic-name
contentParamInHeader:
name: Content-Type
in: header
diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml
index 1f7cce96f..2b70d9489 100755
--- a/cps-ncmp-rest/docs/openapi/ncmp.yml
+++ b/cps-ncmp-rest/docs/openapi/ncmp.yml
@@ -1,5 +1,5 @@
# ============LICENSE_START=======================================================
-# Copyright (C) 2021-2022 Nordix Foundation
+# Copyright (C) 2021-2023 Nordix Foundation
# Modifications Copyright (C) 2021 Pantheon.tech
# Modifications Copyright (C) 2021-2022 Bell Canada
# ================================================================================
@@ -194,6 +194,43 @@ resourceDataForCmHandle:
502:
$ref: 'components.yaml#/components/responses/BadGateway'
+getResourceDataForCmHandleBatch:
+ post:
+ tags:
+ - network-cm-proxy
+ summary: Get resource data for batch of cm handle ids
+ description: This request will be handled asynchronously using messaging to the supplied topic. The rest response will be an acknowledge with a requestId to identify the relevant messages.
+ operationId: getResourceDataForCmHandleBatch
+ parameters:
+ - $ref: 'components.yaml#/components/parameters/datastoreName'
+ - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
+ - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
+ - $ref: 'components.yaml#/components/parameters/requiredTopicParamInQuery'
+ - $ref: 'components.yaml#/components/parameters/includeDescendantsOptionInQuery'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ 400:
+ $ref: 'components.yaml#/components/responses/BadRequest'
+ 401:
+ $ref: 'components.yaml#/components/responses/Unauthorized'
+ 403:
+ $ref: 'components.yaml#/components/responses/Forbidden'
+ 500:
+ $ref: 'components.yaml#/components/responses/InternalServerError'
+ 502:
+ $ref: 'components.yaml#/components/responses/BadGateway'
+
queryResourceDataForCmHandle:
get:
tags:
diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml
index ee2936690..5b4c0d349 100755
--- a/cps-ncmp-rest/docs/openapi/openapi.yml
+++ b/cps-ncmp-rest/docs/openapi/openapi.yml
@@ -1,5 +1,5 @@
# ============LICENSE_START=======================================================
-# Copyright (C) 2021-2022 Nordix Foundation
+# Copyright (C) 2021-2023 Nordix Foundation
# Modifications Copyright (C) 2021 Pantheon.tech
# Modifications Copyright (C) 2021 Bell Canada
# ================================================================================
@@ -34,6 +34,9 @@ paths:
/v1/ch/{cm-handle}/data/ds/{datastore-name}:
$ref: 'ncmp.yml#/resourceDataForCmHandle'
+ /v1/batch/data/ds/{datastore-name}:
+ $ref: 'ncmp.yml#/getResourceDataForCmHandleBatch'
+
/v1/ch/{cm-handle}/data/ds/{datastore-name}/query:
$ref: 'ncmp.yml#/queryResourceDataForCmHandle'
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 4da251f3c..a8bc3aec7 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
@@ -23,10 +23,10 @@
package org.onap.cps.ncmp.rest.controller;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.DELETE;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE;
import java.util.Collection;
import java.util.List;
@@ -74,12 +74,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
/**
* Get resource data from datastore.
*
- * @param datastoreName name of the datastore
- * @param cmHandle cm handle identifier
- * @param resourceIdentifier resource identifier
- * @param optionsParamInQuery options query parameter
- * @param topicParamInQuery topic query parameter
- * @param includeDescendants whether include descendants
+ * @param datastoreName name of the datastore
+ * @param cmHandle cm handle identifier
+ * @param resourceIdentifier resource identifier
+ * @param optionsParamInQuery options query parameter
+ * @param topicParamInQuery topic query parameter
+ * @param includeDescendantsAsObject whether include descendants
* @return {@code ResponseEntity} response from dmi plugin
*/
@@ -89,25 +89,48 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final String resourceIdentifier,
final String optionsParamInQuery,
final String topicParamInQuery,
- final Boolean includeDescendants) {
+ final Boolean includeDescendantsAsObject) {
final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler =
- ncmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+ ncmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
DatastoreType.fromDatastoreName(datastoreName));
+ final boolean includeDescendants = toPrimitiveFlag(includeDescendantsAsObject);
+
return ncmpDatastoreRequestHandler.executeRequest(cmHandle, resourceIdentifier,
optionsParamInQuery, topicParamInQuery, includeDescendants);
}
+ @Override
+ public ResponseEntity<Object> getResourceDataForCmHandleBatch(final String resourceIdentifier,
+ final String topicParamInQuery,
+ final String datastoreName,
+ final Object requestBody,
+ final String optionsParamInQuery,
+ final Boolean includeDescendantsAsObject) {
+
+ final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler =
+ ncmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
+ DatastoreType.fromDatastoreName(datastoreName));
+
+ final List<String> cmHandleIds = jsonObjectMapper.convertJsonString(jsonObjectMapper.asJsonString(requestBody),
+ List.class);
+
+ final boolean includeDescendants = toPrimitiveFlag(includeDescendantsAsObject);
+
+ return ncmpDatastoreRequestHandler.executeRequest(cmHandleIds, resourceIdentifier,
+ optionsParamInQuery, topicParamInQuery, includeDescendants);
+ }
+
/**
* Query resource data from datastore.
*
- * @param datastoreName name of the datastore
- * @param cmHandle cm handle identifier
- * @param cpsPath CPS Path
- * @param optionsParamInQuery options query parameter
- * @param topicParamInQuery topic query parameter
- * @param includeDescendants whether include descendants
+ * @param datastoreName name of the datastore
+ * @param cmHandle cm handle identifier
+ * @param cpsPath CPS Path
+ * @param optionsParamInQuery options query parameter
+ * @param topicParamInQuery topic query parameter
+ * @param includeDescendantsAsObject whether include descendants
* @return {@code ResponseEntity} response from dmi plugin
*/
@@ -117,13 +140,15 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final String cpsPath,
final String optionsParamInQuery,
final String topicParamInQuery,
- final Boolean includeDescendants) {
+ final Boolean includeDescendantsAsObject) {
validateDataStore(DatastoreType.OPERATIONAL, datastoreName);
- final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler =
- ncmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceQueryHandler();
+ final NcmpDatastoreRequestHandler ncmpCachedResourceRequestHandler =
+ ncmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
+ DatastoreType.fromDatastoreName(datastoreName));
- return ncmpDatastoreRequestHandler.executeRequest(cmHandle, cpsPath, optionsParamInQuery,
- topicParamInQuery, includeDescendants);
+ final boolean includeDescendants = toPrimitiveFlag(includeDescendantsAsObject);
+
+ return ncmpCachedResourceRequestHandler.executeRequest(cmHandle, cpsPath, includeDescendants);
}
/**
@@ -367,5 +392,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
throw new InvalidDatastoreException(requestedDatastoreName + " is not supported");
}
}
+
+ private static boolean toPrimitiveFlag(final Boolean includeDescendantsAsObject) {
+ if (includeDescendantsAsObject == null) {
+ return false;
+ }
+ return includeDescendantsAsObject.booleanValue();
+ }
}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java
new file mode 100644
index 000000000..620f64782
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java
@@ -0,0 +1,70 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022-2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.function.Supplier;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import org.onap.cps.ncmp.api.NetworkCmProxyQueryService;
+import org.onap.cps.spi.FetchDescendantsOption;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class NcmpCachedResourceRequestHandler extends NcmpDatastoreRequestHandler {
+
+ @Setter
+ private String dataStoreName;
+ private final NetworkCmProxyQueryService networkCmProxyQueryService;
+
+ @Override
+ public Supplier<Object> getTaskSupplierForGetRequest(final String cmHandleId,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final boolean includeDescendants) {
+
+ final FetchDescendantsOption fetchDescendantsOption =
+ TaskManagementDefaultHandler.getFetchDescendantsOption(includeDescendants);
+
+ return () -> networkCmProxyDataService.getResourceDataForCmHandle(dataStoreName, cmHandleId, resourceIdentifier,
+ fetchDescendantsOption);
+ }
+
+ /**
+ * Gets ncmp datastore query handler.
+ * Note. Currently only ncmp-datastore:operational supports query operations
+ * @return a ncmp datastore query handler.
+ */
+ @Override
+ public Supplier<Object> getTaskSupplierForQueryRequest(final String cmHandleId,
+ final String resourceIdentifier,
+ final boolean includeDescendants) {
+
+ final FetchDescendantsOption fetchDescendantsOption =
+ TaskManagementDefaultHandler.getFetchDescendantsOption(includeDescendants);
+
+ return () -> networkCmProxyQueryService.queryResourceDataOperational(cmHandleId, resourceIdentifier,
+ fetchDescendantsOption);
+ }
+
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java
deleted file mode 100644
index 0586d4262..000000000
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.rest.controller.handlers;
-
-import java.util.function.Supplier;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.NetworkCmProxyQueryService;
-import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
-import org.onap.cps.spi.FetchDescendantsOption;
-
-@Slf4j
-public class NcmpDatastoreOperationalQueryHandler extends NcmpDatastoreRequestHandler {
-
- private final NetworkCmProxyQueryService networkCmProxyQueryService;
-
- public NcmpDatastoreOperationalQueryHandler(final NetworkCmProxyQueryService networkCmProxyQueryService,
- final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
- final int timeOutInMilliSeconds,
- final boolean notificationFeatureEnabled) {
- super(null, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
- this.networkCmProxyQueryService = networkCmProxyQueryService;
- }
-
- @Override
- public Supplier<Object> getTaskSupplier(final String cmHandle,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId,
- final Boolean includeDescendant) {
-
- final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendant);
-
- return () -> networkCmProxyQueryService.queryResourceDataOperational(cmHandle, resourceIdentifier,
- fetchDescendantsOption);
- }
-
-}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java
deleted file mode 100644
index a4720b22f..000000000
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.rest.controller.handlers;
-
-import java.util.function.Supplier;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
-import org.onap.cps.spi.FetchDescendantsOption;
-
-@Slf4j
-public class NcmpDatastoreOperationalResourceRequestHandler extends NcmpDatastoreRequestHandler {
-
- public NcmpDatastoreOperationalResourceRequestHandler(final NetworkCmProxyDataService networkCmProxyDataService,
- final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
- final int timeOutInMilliSeconds,
- final boolean notificationFeatureEnabled) {
- super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
- }
-
- @Override
- public Supplier<Object> getTaskSupplier(final String cmHandle,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId,
- final Boolean includeDescendant) {
-
- final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendant);
-
- return () -> networkCmProxyDataService.getResourceDataOperational(cmHandle, resourceIdentifier,
- fetchDescendantsOption);
- }
-
-}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java
deleted file mode 100644
index 1445e3e27..000000000
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.rest.controller.handlers;
-
-import java.util.function.Supplier;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
-
-@Slf4j
-public class NcmpDatastorePassthroughOperationalResourceRequestHandler extends NcmpDatastoreRequestHandler {
-
- public NcmpDatastorePassthroughOperationalResourceRequestHandler(
- final NetworkCmProxyDataService networkCmProxyDataService,
- final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
- final int timeOutInMilliSeconds,
- final boolean notificationFeatureEnabled) {
- super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
- }
-
- @Override
- public Supplier<Object> getTaskSupplier(final String cmHandle,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId,
- final Boolean includeDescendant) {
-
- return () -> networkCmProxyDataService.getResourceDataOperationalForCmHandle(
- cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
- }
-
-}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java
deleted file mode 100644
index 8194ec9fd..000000000
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.rest.controller.handlers;
-
-import java.util.function.Supplier;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
-
-@Slf4j
-public class NcmpDatastorePassthroughRunningResourceRequestHandler extends NcmpDatastoreRequestHandler {
-
- public NcmpDatastorePassthroughRunningResourceRequestHandler(
- final NetworkCmProxyDataService networkCmProxyDataService,
- final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
- final int timeOutInMilliSeconds,
- final boolean notificationFeatureEnabled) {
- super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
- }
-
- @Override
- public Supplier<Object> getTaskSupplier(final String cmHandle,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId,
- final Boolean includeDescendant) {
-
- return () -> networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
- cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
- }
-}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java
index 850200396..050e724b2 100644
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,38 +20,37 @@
package org.onap.cps.ncmp.rest.controller.handlers;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
-import lombok.RequiredArgsConstructor;
+import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
import org.onap.cps.ncmp.rest.util.TopicValidator;
-import org.onap.cps.spi.FetchDescendantsOption;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
-@RequiredArgsConstructor
+@NoArgsConstructor
@Slf4j
-public abstract class NcmpDatastoreRequestHandler {
+@Service
+public class NcmpDatastoreRequestHandler implements TaskManagementDefaultHandler {
- private static final String NO_REQUEST_ID = null;
- private static final String NO_TOPIC = null;
-
- protected final NetworkCmProxyDataService networkCmProxyDataService;
- protected final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
- protected final int timeOutInMilliSeconds;
- protected final boolean notificationFeatureEnabled;
-
- protected abstract Supplier<Object> getTaskSupplier(final String cmHandle,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId,
- final Boolean includeDescendant);
+ @Value("${notification.async.executor.time-out-value-in-ms:2000}")
+ protected int timeOutInMilliSeconds;
+ @Value("${notification.enabled:true}")
+ protected boolean notificationFeatureEnabled;
+ @Autowired
+ protected NetworkCmProxyDataService networkCmProxyDataService;
+ @Autowired
+ protected CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
/**
- * Execute a request on a datastore.
+ * Executes synchronous/asynchronous request for given cm handle.
*
* @param cmHandleId the cm handle
* @param resourceIdentifier the resource identifier
@@ -64,25 +63,62 @@ public abstract class NcmpDatastoreRequestHandler {
final String resourceIdentifier,
final String optionsParamInQuery,
final String topicParamInQuery,
- final Boolean includeDescendants) {
+ final boolean includeDescendants) {
final boolean asyncResponseRequested = topicParamInQuery != null;
if (asyncResponseRequested && notificationFeatureEnabled) {
- final String requestId = UUID.randomUUID().toString();
- final Supplier<Object> taskSupplier = getTaskSupplier(cmHandleId, resourceIdentifier, optionsParamInQuery,
- topicParamInQuery, requestId, includeDescendants);
- return executeTaskAsync(topicParamInQuery, requestId, taskSupplier);
+ return executeAsyncTaskAndGetResponseEntity(cmHandleId, resourceIdentifier, optionsParamInQuery,
+ topicParamInQuery, includeDescendants, false);
}
if (asyncResponseRequested) {
log.warn("Asynchronous request is unavailable as notification feature is currently disabled, "
- + "will use synchronous operation.");
+ + "will use synchronous operation.");
}
- final Supplier<Object> taskSupplier = getTaskSupplier(cmHandleId, resourceIdentifier, optionsParamInQuery,
- NO_TOPIC, NO_REQUEST_ID, includeDescendants);
+ final Supplier<Object> taskSupplier = getTaskSupplierForGetRequest(cmHandleId,
+ resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID, includeDescendants);
return executeTaskSync(taskSupplier);
}
+ /**
+ * Executes a synchronous request for given cm handle.
+ * Note. Currently only ncmp-datastore:operational supports query operations.
+ *
+ * @param cmHandleId the cm handle
+ * @param resourceIdentifier the resource identifier
+ * @param includeDescendants whether include descendants
+ * @return the response entity
+ */
+ public ResponseEntity<Object> executeRequest(final String cmHandleId,
+ final String resourceIdentifier,
+ final boolean includeDescendants) {
+
+ final Supplier<Object> taskSupplier = getTaskSupplierForQueryRequest(cmHandleId, resourceIdentifier,
+ includeDescendants);
+ return executeTaskSync(taskSupplier);
+ }
+
+ /**
+ * Executes synchronous/asynchronous request for batch of cm handles.
+ *
+ * @param cmHandleIds list of cm handles
+ * @param resourceIdentifier the resource identifier
+ * @param optionsParamInQuery the options param in query
+ * @param topicParamInQuery the topic param in query
+ * @param includeDescendants whether include descendants
+ * @return the response entity
+ */
+ public ResponseEntity<Object> executeRequest(final List<String> cmHandleIds,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final boolean includeDescendants) {
+
+ return executeAsyncTaskAndGetResponseEntity(cmHandleIds, resourceIdentifier, optionsParamInQuery,
+ topicParamInQuery, includeDescendants, true);
+
+ }
+
protected ResponseEntity<Object> executeTaskAsync(final String topicParamInQuery,
final String requestId,
final Supplier<Object> taskSupplier) {
@@ -98,9 +134,25 @@ public abstract class NcmpDatastoreRequestHandler {
return ResponseEntity.ok(taskSupplier.get());
}
- protected static FetchDescendantsOption getFetchDescendantsOption(final Boolean includeDescendant) {
- return Boolean.TRUE.equals(includeDescendant) ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
- : FetchDescendantsOption.OMIT_DESCENDANTS;
+ private ResponseEntity<Object> executeAsyncTaskAndGetResponseEntity(final Object targetObject,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final boolean includeDescendants,
+ final boolean isBulkRequest) {
+ final String requestId = UUID.randomUUID().toString();
+ final Supplier<Object> taskSupplier;
+ if (isBulkRequest) {
+ taskSupplier = getTaskSupplierForBulkRequest((List<String>) targetObject,
+ resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId, includeDescendants);
+ } else {
+ taskSupplier = getTaskSupplierForGetRequest(targetObject.toString(), resourceIdentifier,
+ optionsParamInQuery, topicParamInQuery, requestId, includeDescendants);
+ }
+ if (taskSupplier == NO_OBJECT_SUPPLIER) {
+ return new ResponseEntity<>(Map.of("status", "Unable to execute request as "
+ + "datastore is not implemented."), HttpStatus.NOT_IMPLEMENTED);
+ }
+ return executeTaskAsync(topicParamInQuery, requestId, taskSupplier);
}
-
}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java
index ff7bda6a4..9a71798fa 100644
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,24 +21,13 @@
package org.onap.cps.ncmp.rest.controller.handlers;
import lombok.RequiredArgsConstructor;
-import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.api.NetworkCmProxyQueryService;
-import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class NcmpDatastoreResourceRequestHandlerFactory {
-
- private final NetworkCmProxyDataService networkCmProxyDataService;
- private final NetworkCmProxyQueryService networkCmProxyQueryService;
- private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
-
- @Value("${notification.async.executor.time-out-value-in-ms:2000}")
- private int timeOutInMilliSeconds;
- @Value("${notification.enabled:true}")
- private boolean notificationFeatureEnabled;
+ private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler;
+ private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler;
/**
* Gets ncmp datastore handler.
@@ -46,31 +35,17 @@ public class NcmpDatastoreResourceRequestHandlerFactory {
* @param datastoreType the datastore type
* @return the ncmp datastore handler
*/
- public NcmpDatastoreRequestHandler getNcmpDatastoreResourceRequestHandler(
- final DatastoreType datastoreType) {
+ public NcmpDatastoreRequestHandler getNcmpResourceRequestHandler(final DatastoreType datastoreType) {
switch (datastoreType) {
case OPERATIONAL:
- return new NcmpDatastoreOperationalResourceRequestHandler(networkCmProxyDataService,
- cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+ ncmpCachedResourceRequestHandler.setDataStoreName(datastoreType.getDatastoreName());
+ return ncmpCachedResourceRequestHandler;
case PASSTHROUGH_RUNNING:
- return new NcmpDatastorePassthroughRunningResourceRequestHandler(networkCmProxyDataService,
- cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
case PASSTHROUGH_OPERATIONAL:
default:
- return new NcmpDatastorePassthroughOperationalResourceRequestHandler(networkCmProxyDataService,
- cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+ ncmpPassthroughResourceRequestHandler.setDataStoreName(datastoreType.getDatastoreName());
+ return ncmpPassthroughResourceRequestHandler;
}
}
-
- /**
- * Gets ncmp datastore query handler.
- * Note. Currently only ncmp-datastore:operational supports query operations
- * @return a ncmp datastore query handler.
- */
- public NcmpDatastoreRequestHandler getNcmpDatastoreResourceQueryHandler() {
- return new NcmpDatastoreOperationalQueryHandler(networkCmProxyQueryService, cpsNcmpTaskExecutor,
- timeOutInMilliSeconds, notificationFeatureEnabled);
- }
-
}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java
new file mode 100644
index 000000000..ab5d587e9
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java
@@ -0,0 +1,58 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022-2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.List;
+import java.util.function.Supplier;
+import lombok.Setter;
+import org.springframework.stereotype.Component;
+
+@Component
+public class NcmpPassthroughResourceRequestHandler extends NcmpDatastoreRequestHandler {
+
+ @Setter
+ private String dataStoreName;
+
+ @Override
+ public Supplier<Object> getTaskSupplierForGetRequest(final String cmHandleId,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final boolean includeDescendants) {
+
+ return () -> networkCmProxyDataService.getResourceDataForCmHandle(
+ dataStoreName, cmHandleId, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
+ }
+
+ @Override
+ public Supplier<Object> getTaskSupplierForBulkRequest(final List<String> cmHandleIds,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final boolean includeDescendants) {
+
+ return () -> networkCmProxyDataService.getResourceDataForCmHandleBatch(
+ dataStoreName, cmHandleIds, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
+ }
+
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java
new file mode 100644
index 000000000..08e8407c6
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java
@@ -0,0 +1,63 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.List;
+import java.util.function.Supplier;
+import org.onap.cps.spi.FetchDescendantsOption;
+
+public interface TaskManagementDefaultHandler {
+
+ String NO_REQUEST_ID = null;
+ String NO_TOPIC = null;
+ Supplier<Object> NO_OBJECT_SUPPLIER = null;
+
+ default Supplier<Object> getTaskSupplierForGetRequest(final String cmHandleId,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final boolean includeDescendant) {
+ return NO_OBJECT_SUPPLIER;
+
+ }
+
+ default Supplier<Object> getTaskSupplierForQueryRequest(final String cmHandleId,
+ final String resourceIdentifier,
+ final boolean includeDescendant) {
+ return NO_OBJECT_SUPPLIER;
+
+ }
+
+ default Supplier<Object> getTaskSupplierForBulkRequest(final List<String> cmHandleIds,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final boolean includeDescendant) {
+ return NO_OBJECT_SUPPLIER;
+ }
+
+ static FetchDescendantsOption getFetchDescendantsOption(final boolean includeDescendants) {
+ return includeDescendants ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+ : FetchDescendantsOption.OMIT_DESCENDANTS;
+ }
+}
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 d67804e12..9531101e1 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-2022 Nordix Foundation
+ * Modifications Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2021-2022 Bell Canada.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,18 +33,13 @@ import org.onap.cps.ncmp.api.inventory.CompositeState
import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
import org.onap.cps.ncmp.api.inventory.LockReasonCategory
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
-import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType
-import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreOperationalQueryHandler
-import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreOperationalResourceRequestHandler
-import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughOperationalResourceRequestHandler
-import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughRunningResourceRequestHandler
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpCachedResourceRequestHandler
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpPassthroughResourceRequestHandler
import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory
-import org.onap.cps.ncmp.rest.exceptions.InvalidDatastoreException
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
import org.onap.cps.ncmp.rest.util.DeprecationHelper
import org.onap.cps.spi.FetchDescendantsOption
-import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.ModuleDefinition
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.utils.JsonObjectMapper
@@ -55,31 +50,32 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
-import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
import spock.lang.Shared
import spock.lang.Specification
-
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores
import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.DELETE
+import static org.onap.cps.ncmp.rest.controller.handlers.DatastoreType.PASSTHROUGH_OPERATIONAL
+import static org.onap.cps.ncmp.rest.controller.handlers.DatastoreType.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.rest.controller.handlers.DatastoreType.OPERATIONAL
@WebMvcTest(NetworkCmProxyController)
class NetworkCmProxyControllerSpec extends Specification {
- public static final int TIMEOUT_IN_MS = 2000
- public static final boolean NOTIFICATION_ENABLED = true
+ private static final int TIMEOUT_IN_MS = 2000
+ private static final boolean NOTIFICATION_ENABLED = true
@Autowired
MockMvc mvc
@@ -115,6 +111,7 @@ class NetworkCmProxyControllerSpec extends Specification {
def ncmpBasePathV1
def requestBody = '{"some-key":"some-value"}'
+ def bulkRequestBody = '["testCmHandle"]'
@Shared
def NO_TOPIC = null
@@ -124,24 +121,15 @@ class NetworkCmProxyControllerSpec extends Specification {
.format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
void setup() {
- stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
- DatastoreType.OPERATIONAL) >>
- new NcmpDatastoreOperationalResourceRequestHandler(
- mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+ stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
+ OPERATIONAL) >> getNcmpDatastoreRequestHandler(OPERATIONAL,new NcmpCachedResourceRequestHandler(mockNetworkCmProxyQueryService))
- stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
- DatastoreType.PASSTHROUGH_OPERATIONAL) >>
- new NcmpDatastorePassthroughOperationalResourceRequestHandler(
- mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+ stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
+ PASSTHROUGH_OPERATIONAL) >> getNcmpDatastoreRequestHandler(PASSTHROUGH_OPERATIONAL,new NcmpPassthroughResourceRequestHandler())
- stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
- DatastoreType.PASSTHROUGH_RUNNING) >>
- new NcmpDatastorePassthroughRunningResourceRequestHandler(
- mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+ stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
+ PASSTHROUGH_RUNNING) >> getNcmpDatastoreRequestHandler(PASSTHROUGH_RUNNING,new NcmpPassthroughResourceRequestHandler())
- stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceQueryHandler() >>
- new NcmpDatastoreOperationalQueryHandler(mockNetworkCmProxyQueryService, spiedCpsTaskExecutor,
- TIMEOUT_IN_MS, NOTIFICATION_ENABLED);
}
def 'Get Resource Data from pass-through operational.'() {
@@ -154,11 +142,8 @@ class NetworkCmProxyControllerSpec extends Specification {
.contentType(MediaType.APPLICATION_JSON)
).andReturn().response
then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle'
- 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle',
- 'parent/child',
- '(a=1,b=2)',
- NO_TOPIC,
- NO_REQUEST_ID)
+ 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle',
+ 'parent/child','(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID)
and: 'response status is Ok'
response.status == HttpStatus.OK.value()
}
@@ -205,6 +190,48 @@ class NetworkCmProxyControllerSpec extends Specification {
'invalid non-empty topic value in url' | 'passthrough-running' | '&topic=1_5_*_#'
}
+ def 'Get bulk resource data for #datastoreName from dmi service.'() {
+ given: 'bulk resource data url'
+ def getUrl = "$ncmpBasePathV1/batch/data/ds/${datastoreName}" +
+ "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=myTopic"
+ when: 'post data resource request is performed'
+ def response = mvc.perform(
+ post(getUrl)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(bulkRequestBody)
+ ).andReturn().response
+ then: 'response status is Ok'
+ response.status == HttpStatus.OK.value()
+ // TODO Need to be un-commented as it's failing into onap CICD pipeline
+ // but passed into nordix and local build.
+ //and: 'the NCMP data service is called with getResourceDataForCmHandleBatch'
+ // 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandleBatch(datastoreName, ['testCmHandle'],
+ // 'parent/child',
+ // '(a=1,b=2)',
+ // 'myTopic',
+ // _)
+ and: 'async request id is generated'
+ assert response.contentAsString.contains("requestId")
+ where: 'the following data stores are used'
+ datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
+ }
+
+ def 'Get bulk resource data for non-supported #datastoreName from dmi service.'() {
+ given: 'bulk resource data url'
+ def getUrl = "$ncmpBasePathV1/batch/data/ds/ncmp-datastore:operational" +
+ "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=myTopic"
+ when: 'post data resource request is performed'
+ def response = mvc.perform(
+ post(getUrl)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(bulkRequestBody)
+ ).andReturn().response
+ then: 'response status code is 501 not implemented'
+ response.status == HttpStatus.NOT_IMPLEMENTED.value()
+ where: 'the following data store is un-supported'
+ datastoreName << [OPERATIONAL.datastoreName]
+ }
+
def 'Query Resource Data from operational.'() {
given: 'the query resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational/query" +
@@ -244,11 +271,8 @@ class NetworkCmProxyControllerSpec extends Specification {
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
"?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
and: 'ncmp service returns json object'
- mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
- resourceIdentifier,
- '(a=1,b=2)',
- NO_TOPIC,
- NO_REQUEST_ID) >> '{valid-json}'
+ mockNetworkCmProxyDataService.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle',
+ resourceIdentifier,'(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID) >> '{valid-json}'
when: 'get data resource request is performed'
def response = mvc.perform(
get(getUrl)
@@ -531,9 +555,7 @@ class NetworkCmProxyControllerSpec extends Specification {
.contentType(MediaType.APPLICATION_JSON)
).andReturn().response
then: 'the NCMP data service is called with getResourceDataOperational with #descendantsOption'
- 1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle',
- 'parent/child',
- descendantsOption)
+ 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(OPERATIONAL.datastoreName, 'testCmHandle', 'parent/child', descendantsOption)
and: 'response status is Ok'
response.status == HttpStatus.OK.value()
where: 'the following parameters are used'
@@ -626,5 +648,19 @@ class NetworkCmProxyControllerSpec extends Specification {
return assertContainsAll(response, expectedContent)
}
+ def getNcmpDatastoreRequestHandler(dataStoreType, ncmpDatastoreRequestHandler) {
+ if (ncmpDatastoreRequestHandler instanceof NcmpCachedResourceRequestHandler) {
+ NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler = (NcmpCachedResourceRequestHandler) ncmpDatastoreRequestHandler
+ ncmpCachedResourceRequestHandler.dataStoreName = dataStoreType.datastoreName
+ } else {
+ NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler = (NcmpPassthroughResourceRequestHandler) ncmpDatastoreRequestHandler
+ ncmpPassthroughResourceRequestHandler.dataStoreName = dataStoreType.datastoreName
+ }
+ ncmpDatastoreRequestHandler.networkCmProxyDataService = mockNetworkCmProxyDataService
+ ncmpDatastoreRequestHandler.cpsNcmpTaskExecutor = spiedCpsTaskExecutor
+ ncmpDatastoreRequestHandler.notificationFeatureEnabled = NOTIFICATION_ENABLED
+ ncmpDatastoreRequestHandler.timeOutInMilliSeconds = TIMEOUT_IN_MS
+ return ncmpDatastoreRequestHandler
+ }
}
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy
index 7c504981e..15b3ee6c1 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,21 +20,28 @@
package org.onap.cps.ncmp.rest.controller.handlers
+import org.spockframework.spring.SpringBean
import spock.lang.Specification
class NcmpDatastoreRequestHandlerFactorySpec extends Specification {
- def objectUnderTest = new NcmpDatastoreResourceRequestHandlerFactory(null, null, null)
+ @SpringBean
+ NcmpCachedResourceRequestHandler mockNcmpCachedResourceRequestHandler = new NcmpCachedResourceRequestHandler(null)
+
+ @SpringBean
+ NcmpPassthroughResourceRequestHandler mockNcmpPassthroughResourceRequestHandler = new NcmpPassthroughResourceRequestHandler()
+
+ def objectUnderTest = new NcmpDatastoreResourceRequestHandlerFactory(mockNcmpCachedResourceRequestHandler, mockNcmpPassthroughResourceRequestHandler)
def 'Creating ncmp datastore request handlers.'() {
when: 'a ncmp datastore request handler is created for #datastoreType'
- def result = objectUnderTest.getNcmpDatastoreResourceRequestHandler(datastoreType)
+ def result = objectUnderTest.getNcmpResourceRequestHandler(datastoreType)
then: 'the result is of the expected class'
result.class == expectedClass
where: 'the following type of datastore is used'
datastoreType || expectedClass
- DatastoreType.OPERATIONAL || NcmpDatastoreOperationalResourceRequestHandler
- DatastoreType.PASSTHROUGH_OPERATIONAL || NcmpDatastorePassthroughOperationalResourceRequestHandler
- DatastoreType.PASSTHROUGH_RUNNING || NcmpDatastorePassthroughRunningResourceRequestHandler
+ DatastoreType.OPERATIONAL || NcmpCachedResourceRequestHandler
+ DatastoreType.PASSTHROUGH_OPERATIONAL || NcmpPassthroughResourceRequestHandler
+ DatastoreType.PASSTHROUGH_RUNNING || NcmpPassthroughResourceRequestHandler
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
index 128eed3f2..03737bc51 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
@@ -23,10 +23,10 @@
package org.onap.cps.ncmp.api;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
-
import java.util.Collection;
+import java.util.List;
import java.util.Map;
+import org.onap.cps.ncmp.api.impl.operations.OperationEnum;
import org.onap.cps.ncmp.api.inventory.CompositeState;
import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters;
@@ -51,50 +51,55 @@ public interface NetworkCmProxyDataService {
DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration);
/**
- * Get resource data for data store pass-through operational
- * using dmi.
+ * Get resource data for given data store using dmi.
*
- * @param cmHandleId cm handle identifier
- * @param resourceIdentifier resource identifier
+ * @param dataStoreName data store name
+ * @param cmHandleId cm handle identifier
+ * @param resourceIdentifier resource identifier
* @param optionsParamInQuery options query
- * @param topicParamInQuery topic name for (triggering) async responses
- * @param requestId unique requestId for async request
+ * @param topicParamInQuery topic name for (triggering) async responses
+ * @param requestId unique requestId for async request
* @return {@code Object} resource data
*/
- Object getResourceDataOperationalForCmHandle(String cmHandleId,
- String resourceIdentifier,
- String optionsParamInQuery,
- String topicParamInQuery,
- String requestId);
+ Object getResourceDataForCmHandle(String dataStoreName,
+ String cmHandleId,
+ String resourceIdentifier,
+ String optionsParamInQuery,
+ String topicParamInQuery,
+ String requestId);
/**
* Get resource data for operational.
*
+ * @param dataStoreName data store name
* @param cmHandleId cm handle identifier
* @param resourceIdentifier resource identifier
* @Link FetchDescendantsOption fetch descendants option
* @return {@code Object} resource data
*/
- Object getResourceDataOperational(String cmHandleId,
+ Object getResourceDataForCmHandle(String dataStoreName,
+ String cmHandleId,
String resourceIdentifier,
FetchDescendantsOption fetchDescendantsOption);
/**
- * Get resource data for data store pass-through running
- * using dmi.
+ * Get resource data for given batch of cm handles using dmi.
*
- * @param cmHandleId cm handle identifier
- * @param resourceIdentifier resource identifier
+ * @param dataStoreName data store name
+ * @param cmHandleIds cm handle identifiers
+ * @param resourceIdentifier resource identifier
* @param optionsParamInQuery options query
- * @param topicParamInQuery topic name for (triggering) async responses
- * @param requestId unique requestId for async request
+ * @param topicParamInQuery topic name for (triggering) async responses
+ * @param requestId unique requestId for async request
* @return {@code Object} resource data
*/
- Object getResourceDataPassThroughRunningForCmHandle(String cmHandleId,
- String resourceIdentifier,
- String optionsParamInQuery,
- String topicParamInQuery,
- String requestId);
+ Object getResourceDataForCmHandleBatch(String dataStoreName,
+ List<String> cmHandleIds,
+ String resourceIdentifier,
+ String optionsParamInQuery,
+ String topicParamInQuery,
+ String requestId);
+
/**
* Write resource data for data store pass-through running
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
index b3904bd0b..1b1997f23 100755
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
@@ -25,7 +25,6 @@
package org.onap.cps.ncmp.api.impl;
import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCmHandleQueryParameters;
import com.google.common.collect.Lists;
@@ -46,7 +45,7 @@ import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService;
import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler;
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
-import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
+import org.onap.cps.ncmp.api.impl.operations.OperationEnum;
import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions;
import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
@@ -115,38 +114,41 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
}
@Override
- public Object getResourceDataOperationalForCmHandle(final String cmHandleId,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId) {
- final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId,
+ public Object getResourceDataForCmHandle(final String dataStoreName,
+ final String cmHandleId,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId) {
+ final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(dataStoreName, cmHandleId,
resourceIdentifier,
optionsParamInQuery,
- DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
- requestId, topicParamInQuery);
+ topicParamInQuery,
+ requestId);
return responseEntity.getBody();
}
@Override
- public Object getResourceDataOperational(final String cmHandleId,
+ public Object getResourceDataForCmHandle(final String dataStoreName,
+ final String cmHandleId,
final String resourceIdentifier,
final FetchDescendantsOption fetchDescendantsOption) {
- return cpsDataService.getDataNodes(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, resourceIdentifier,
+ return cpsDataService.getDataNodes(dataStoreName, cmHandleId, resourceIdentifier,
fetchDescendantsOption).iterator().next();
}
@Override
- public Object getResourceDataPassThroughRunningForCmHandle(final String cmHandleId,
- final String resourceIdentifier,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId) {
- final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId,
+ public Object getResourceDataForCmHandleBatch(final String dataStoreName,
+ final List<String> cmHandleIds,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId) {
+ final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(dataStoreName, cmHandleIds,
resourceIdentifier,
optionsParamInQuery,
- DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING,
- requestId, topicParamInQuery);
+ topicParamInQuery,
+ requestId);
return responseEntity.getBody();
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
index d5b459b02..9d087806c 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ package org.onap.cps.ncmp.api.impl.client;
import lombok.AllArgsConstructor;
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration.DmiProperties;
import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException;
-import org.onap.cps.ncmp.api.impl.operations.DmiRequestBody;
+import org.onap.cps.ncmp.api.impl.operations.OperationEnum;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -43,14 +43,14 @@ public class DmiRestClient {
/**
* Sends POST operation to DMI with json body containing module references.
* @param dmiResourceUrl dmi resource url
- * @param jsonData json data body
+ * @param requestBodyAsJsonString json data body
* @param operation the type of operation being executed (for error reporting only)
* @return response entity of type String
*/
public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl,
- final String jsonData,
- final DmiRequestBody.OperationEnum operation) {
- final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(new HttpHeaders()));
+ final String requestBodyAsJsonString,
+ final OperationEnum operation) {
+ final var httpEntity = new HttpEntity<>(requestBodyAsJsonString, configureHttpHeaders(new HttpHeaders()));
try {
return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class);
} catch (final HttpStatusCodeException httpStatusCodeException) {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java
index ac2bd4596..ff7afc9eb 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java
@@ -20,14 +20,12 @@
package org.onap.cps.ncmp.api.impl.config.embeddedcache;
-import com.hazelcast.config.Config;
import com.hazelcast.config.MapConfig;
-import com.hazelcast.config.NamedConfig;
import com.hazelcast.config.QueueConfig;
-import com.hazelcast.core.Hazelcast;
-import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import java.util.concurrent.BlockingQueue;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.cache.HazelcastCacheConfig;
import org.onap.cps.spi.model.DataNode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -35,13 +33,14 @@ import org.springframework.context.annotation.Configuration;
/**
* Core infrastructure of the hazelcast distributed caches for Module Sync and Data Sync use cases.
*/
+@Slf4j
@Configuration
-public class SynchronizationCacheConfig {
+public class SynchronizationCacheConfig extends HazelcastCacheConfig {
public static final int MODULE_SYNC_STARTED_TTL_SECS = 600;
public static final int DATA_SYNC_SEMAPHORE_TTL_SECS = 1800;
- private static final QueueConfig commonQueueConfig = createQueueConfig();
+ private static final QueueConfig commonQueueConfig = createQueueConfig("defaultQueueConfig");
private static final MapConfig moduleSyncStartedConfig = createMapConfig("moduleSyncStartedConfig");
private static final MapConfig dataSyncSemaphoresConfig = createMapConfig("dataSyncSemaphoresConfig");
@@ -52,7 +51,8 @@ public class SynchronizationCacheConfig {
*/
@Bean
public BlockingQueue<DataNode> moduleSyncWorkQueue() {
- return createHazelcastInstance("moduleSyncWorkQueue", commonQueueConfig)
+ return createHazelcastInstance("moduleSyncWorkQueue", commonQueueConfig,
+ "synchronization-caches")
.getQueue("moduleSyncWorkQueue");
}
@@ -63,7 +63,8 @@ public class SynchronizationCacheConfig {
*/
@Bean
public IMap<String, Object> moduleSyncStartedOnCmHandles() {
- return createHazelcastInstance("moduleSyncStartedOnCmHandles", moduleSyncStartedConfig)
+ return createHazelcastInstance("moduleSyncStartedOnCmHandles", moduleSyncStartedConfig,
+ "synchronization-caches")
.getMap("moduleSyncStartedOnCmHandles");
}
@@ -74,39 +75,8 @@ public class SynchronizationCacheConfig {
*/
@Bean
public IMap<String, Boolean> dataSyncSemaphores() {
- return createHazelcastInstance("dataSyncSemaphores", dataSyncSemaphoresConfig)
+ return createHazelcastInstance("dataSyncSemaphores", dataSyncSemaphoresConfig,
+ "synchronization-caches")
.getMap("dataSyncSemaphores");
}
-
- private HazelcastInstance createHazelcastInstance(
- final String hazelcastInstanceName, final NamedConfig namedConfig) {
- return Hazelcast.newHazelcastInstance(initializeConfig(hazelcastInstanceName, namedConfig));
- }
-
- private Config initializeConfig(final String instanceName, final NamedConfig namedConfig) {
- final Config config = new Config(instanceName);
- if (namedConfig instanceof MapConfig) {
- config.addMapConfig((MapConfig) namedConfig);
- }
- if (namedConfig instanceof QueueConfig) {
- config.addQueueConfig((QueueConfig) namedConfig);
- }
- config.setClusterName("synchronization-caches");
- return config;
- }
-
- private static QueueConfig createQueueConfig() {
- final QueueConfig commonQueueConfig = new QueueConfig("defaultQueueConfig");
- commonQueueConfig.setBackupCount(3);
- commonQueueConfig.setAsyncBackupCount(3);
- return commonQueueConfig;
- }
-
- private static MapConfig createMapConfig(final String configName) {
- final MapConfig mapConfig = new MapConfig(configName);
- mapConfig.setBackupCount(3);
- mapConfig.setAsyncBackupCount(3);
- return mapConfig;
- }
-
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfig.java
new file mode 100644
index 000000000..d2c3dc259
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfig.java
@@ -0,0 +1,51 @@
+/*
+ * ============LICENSE_START========================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.event.avc;
+
+import com.hazelcast.config.MapConfig;
+import com.hazelcast.map.IMap;
+import java.util.Set;
+import org.onap.cps.cache.HazelcastCacheConfig;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Core infrastructure of the hazelcast distributed cache for subscription forward config use cases.
+ */
+@Configuration
+public class ForwardedSubscriptionEventCacheConfig extends HazelcastCacheConfig {
+
+ private static final MapConfig forwardedSubscriptionEventCacheMapConfig =
+ createMapConfig("forwardedSubscriptionEventCacheMapConfig");
+
+ /**
+ * Distributed instance of forwarded subscription information cache that contains subscription event
+ * id by dmi names as properties.
+ *
+ * @return configured map of subscription event ids as keys to sets of dmi names for values
+ */
+ @Bean
+ public IMap<String, Set<String>> forwardedSubscriptionEventCache() {
+ return createHazelcastInstance("hazelCastInstanceSubscriptionEvents",
+ forwardedSubscriptionEventCacheMapConfig, "cps-ncmp-service-caches")
+ .getMap("forwardedSubscriptionEventCache");
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ResponseTimeoutTask.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ResponseTimeoutTask.java
new file mode 100644
index 000000000..e7edecfac
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/ResponseTimeoutTask.java
@@ -0,0 +1,51 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.event.avc;
+
+import com.hazelcast.map.IMap;
+import java.util.Set;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RequiredArgsConstructor
+public class ResponseTimeoutTask implements Runnable {
+
+ private final IMap<String, Set<String>> forwardedSubscriptionEventCache;
+ private final String subscriptionEventId;
+
+ @Override
+ public void run() {
+ if (forwardedSubscriptionEventCache.containsKey(subscriptionEventId)) {
+ final Set<String> dmiNames = forwardedSubscriptionEventCache.get(subscriptionEventId);
+ if (dmiNames.isEmpty()) {
+ //TODO full outcome response here
+ log.info("placeholder to create full outcome response for subscriptionEventId: {}.",
+ subscriptionEventId);
+ } else {
+ //TODO partial outcome response here
+ log.info("placeholder to create partial outcome response for subscriptionEventId: {}.",
+ subscriptionEventId);
+ }
+ forwardedSubscriptionEventCache.remove(subscriptionEventId);
+ }
+ }
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumer.java
new file mode 100644
index 000000000..b332ad1a0
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumer.java
@@ -0,0 +1,82 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.event.avc;
+
+import com.hazelcast.map.IMap;
+import java.util.Set;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.models.SubscriptionEventResponse;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+@RequiredArgsConstructor
+public class SubscriptionEventResponseConsumer {
+
+ private final IMap<String, Set<String>> forwardedSubscriptionEventCache;
+
+ @Value("${app.ncmp.avc.subscription-outcome-topic}")
+ private String subscriptionOutcomeEventTopic;
+
+ @Value("${notification.enabled:true}")
+ private boolean notificationFeatureEnabled;
+
+ @Value("${ncmp.model-loader.subscription:false}")
+ private boolean subscriptionModelLoaderEnabled;
+
+ /**
+ * Consume subscription response event.
+ *
+ * @param subscriptionEventResponse the event to be consumed
+ */
+ @KafkaListener(topics = "${app.ncmp.avc.subscription-response-topic}",
+ properties = {"spring.json.value.default.type=org.onap.cps.ncmp.api.models.SubscriptionEventResponse"})
+ public void consumeSubscriptionEventResponse(final SubscriptionEventResponse subscriptionEventResponse) {
+ log.info("subscription event response of clientId: {} is received.", subscriptionEventResponse.getClientId());
+ final String subscriptionEventId = subscriptionEventResponse.getClientId()
+ + subscriptionEventResponse.getSubscriptionName();
+ final boolean createOutcomeResponse;
+ if (forwardedSubscriptionEventCache.containsKey(subscriptionEventId)) {
+ forwardedSubscriptionEventCache.get(subscriptionEventId).remove(subscriptionEventResponse.getDmiName());
+ createOutcomeResponse = forwardedSubscriptionEventCache.get(subscriptionEventId).isEmpty();
+ if (createOutcomeResponse) {
+ forwardedSubscriptionEventCache.remove(subscriptionEventId);
+ }
+ } else {
+ createOutcomeResponse = true;
+ }
+ if (subscriptionModelLoaderEnabled) {
+ updateSubscriptionEvent(subscriptionEventResponse);
+ }
+ if (createOutcomeResponse && notificationFeatureEnabled) {
+ log.info("placeholder to create full outcome response for subscriptionEventId: {}.", subscriptionEventId);
+ //TODO Create outcome response
+ }
+ }
+
+ private void updateSubscriptionEvent(final SubscriptionEventResponse subscriptionEventResponse) {
+ log.info("placeholder to update persisted subscription for subscriptionEventId: {}.",
+ subscriptionEventResponse.getClientId() + subscriptionEventResponse.getSubscriptionName());
+ }
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java
index c3624b800..4afa051d3 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java
@@ -20,21 +20,28 @@
package org.onap.cps.ncmp.api.impl.events.avcsubscription;
+import com.hazelcast.map.IMap;
import java.util.Collection;
import java.util.Collections;
-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.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.impl.event.avc.ResponseTimeoutTask;
import org.onap.cps.ncmp.api.impl.events.EventsPublisher;
-import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
+import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
import org.onap.cps.ncmp.event.model.SubscriptionEvent;
import org.onap.cps.spi.exceptions.OperationNotYetSupportedException;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -45,9 +52,15 @@ public class SubscriptionEventForwarder {
private final InventoryPersistence inventoryPersistence;
private final EventsPublisher<SubscriptionEvent> eventsPublisher;
+ private final IMap<String, Set<String>> forwardedSubscriptionEventCache;
+
+ private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
private static final String DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX = "ncmp-dmi-cm-avc-subscription-";
+ @Value("${ncmp.timers.subscription-forwarding.dmi-response-timeout-ms:30000}")
+ private int dmiResponseTimeoutInMs;
+
/**
* Forward subscription event.
*
@@ -56,38 +69,41 @@ public class SubscriptionEventForwarder {
public void forwardCreateSubscriptionEvent(final SubscriptionEvent subscriptionEvent) {
final List<Object> cmHandleTargets = subscriptionEvent.getEvent().getPredicates().getTargets();
if (cmHandleTargets == null || cmHandleTargets.isEmpty()
- || cmHandleTargets.stream().anyMatch(id -> ((String) id).contains("*"))) {
+ || cmHandleTargets.stream().anyMatch(id -> ((String) id).contains("*"))) {
throw new OperationNotYetSupportedException(
- "CMHandle targets are required. \"Wildcard\" operations are not yet supported");
+ "CMHandle targets are required. \"Wildcard\" operations are not yet supported");
}
final List<String> cmHandleTargetsAsStrings = cmHandleTargets.stream().map(
- Objects::toString).collect(Collectors.toList());
+ Objects::toString).collect(Collectors.toList());
final Collection<YangModelCmHandle> yangModelCmHandles =
- inventoryPersistence.getYangModelCmHandles(cmHandleTargetsAsStrings);
- final Map<String, Map<String, Map<String, String>>> dmiNameCmHandleMap =
- organizeByDmiName(yangModelCmHandles);
+ inventoryPersistence.getYangModelCmHandles(cmHandleTargetsAsStrings);
+
+ final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName
+ = DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles);
+
+ final Set<String> dmisToRespond = new HashSet<>(dmiPropertiesPerCmHandleIdPerServiceName.keySet());
+ startResponseTimeout(subscriptionEvent, dmisToRespond);
+ forwardEventToDmis(dmiPropertiesPerCmHandleIdPerServiceName, subscriptionEvent);
+ }
+
+ private void forwardEventToDmis(final Map<String, Map<String, Map<String, String>>> dmiNameCmHandleMap,
+ final SubscriptionEvent subscriptionEvent) {
dmiNameCmHandleMap.forEach((dmiName, cmHandlePropertiesMap) -> {
subscriptionEvent.getEvent().getPredicates().setTargets(Collections.singletonList(cmHandlePropertiesMap));
final String eventKey = createEventKey(subscriptionEvent, dmiName);
- eventsPublisher.publishEvent(DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX + dmiName, eventKey, subscriptionEvent);
+ eventsPublisher.publishEvent(
+ DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX + dmiName, eventKey, subscriptionEvent);
});
}
- private Map<String, Map<String, Map<String, String>>> organizeByDmiName(
- final Collection<YangModelCmHandle> yangModelCmHandles) {
- final Map<String, Map<String, Map<String, String>>> dmiNameCmHandlePropertiesMap = new HashMap<>();
- yangModelCmHandles.forEach(cmHandle -> {
- final String dmiName = cmHandle.resolveDmiServiceName(RequiredDmiService.DATA);
- if (!dmiNameCmHandlePropertiesMap.containsKey(dmiName)) {
- final Map<String, Map<String, String>> cmHandleDmiPropertiesMap = new HashMap<>();
- cmHandleDmiPropertiesMap.put(cmHandle.getId(), dmiPropertiesAsMap(cmHandle));
- dmiNameCmHandlePropertiesMap.put(cmHandle.getDmiDataServiceName(), cmHandleDmiPropertiesMap);
- } else {
- dmiNameCmHandlePropertiesMap.get(cmHandle.getDmiDataServiceName())
- .put(cmHandle.getId(), dmiPropertiesAsMap(cmHandle));
- }
- });
- return dmiNameCmHandlePropertiesMap;
+ private void startResponseTimeout(final SubscriptionEvent subscriptionEvent, final Set<String> dmisToRespond) {
+ final String subscriptionEventId = subscriptionEvent.getEvent().getSubscription().getClientID()
+ + subscriptionEvent.getEvent().getSubscription().getName();
+
+ forwardedSubscriptionEventCache.put(subscriptionEventId, dmisToRespond);
+ final ResponseTimeoutTask responseTimeoutTask =
+ new ResponseTimeoutTask(forwardedSubscriptionEventCache, subscriptionEventId);
+ executorService.schedule(responseTimeoutTask, dmiResponseTimeoutInMs, TimeUnit.MILLISECONDS);
}
private String createEventKey(final SubscriptionEvent subscriptionEvent, final String dmiName) {
@@ -98,9 +114,4 @@ public class SubscriptionEventForwarder {
+ dmiName;
}
- public Map<String, String> dmiPropertiesAsMap(final YangModelCmHandle yangModelCmHandle) {
- return yangModelCmHandle.getDmiProperties().stream().collect(
- Collectors.toMap(YangModelCmHandle.Property::getName, YangModelCmHandle.Property::getValue));
- }
-
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java
new file mode 100644
index 000000000..192062fde
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.executor;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class TaskExecutor {
+
+ /**
+ * Execute task asynchronously.
+ *
+ * @param taskSupplier functional method is get() task need to executed asynchronously
+ * @param timeOutInMillis the timeout value in milliseconds
+ */
+ public static CompletableFuture<Object> executeTask(final Supplier<Object> taskSupplier,
+ final long timeOutInMillis) {
+ return CompletableFuture.supplyAsync(taskSupplier::get)
+ .orTimeout(timeOutInMillis, MILLISECONDS);
+ }
+}
+
+
+
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java
new file mode 100644
index 000000000..24edc73f3
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.operations;
+
+import lombok.Getter;
+
+@Getter
+public enum DataStoreEnum {
+ PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"),
+ PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running");
+ private final String value;
+
+ DataStoreEnum(final String value) {
+ this.value = value;
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
index 83faa005f..d648352f1 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,19 +21,24 @@
package org.onap.cps.ncmp.api.impl.operations;
-import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ;
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
+import org.onap.cps.ncmp.api.impl.executor.TaskExecutor;
+import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer;
import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.inventory.CmHandleState;
import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
import org.onap.cps.spi.exceptions.CpsException;
import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
@@ -44,15 +49,14 @@ import org.springframework.stereotype.Component;
@Slf4j
public class DmiDataOperations extends DmiOperations {
- /**
- * Constructor for {@code DmiOperations}. This method also manipulates url properties.
- *
- * @param dmiRestClient {@code DmiRestClient}
- */
+ private static final long DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS = 30000L;
+ private static final String NO_CM_HANDLE_ID = "";
+
public DmiDataOperations(final InventoryPersistence inventoryPersistence,
final JsonObjectMapper jsonObjectMapper,
final NcmpConfiguration.DmiProperties dmiProperties,
- final DmiRestClient dmiRestClient, final DmiServiceUrlBuilder dmiServiceUrlBuilder) {
+ final DmiRestClient dmiRestClient,
+ final DmiServiceUrlBuilder dmiServiceUrlBuilder) {
super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder);
}
@@ -60,48 +64,78 @@ public class DmiDataOperations extends DmiOperations {
* This method fetches the resource data from operational data store for given cm handle
* identifier on given resource using dmi client.
*
- * @param cmHandleId network resource identifier
- * @param resourceId resource identifier
+ * @param dataStoreName name of data store
+ * @param cmHandleId network resource identifier
+ * @param resourceId resource identifier
* @param optionsParamInQuery options query
- * @param dataStore data store enum
- * @param requestId requestId for async responses
* @param topicParamInQuery topic name for (triggering) async responses
+ * @param requestId requestId for async responses
* @return {@code ResponseEntity} response entity
*/
- public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId,
+ public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
+ final String cmHandleId,
final String resourceId,
final String optionsParamInQuery,
- final DataStoreEnum dataStore,
- final String requestId,
- final String topicParamInQuery) {
+ final String topicParamInQuery,
+ final String requestId) {
final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
- final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
- final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, resourceId, optionsParamInQuery, dataStore,
- topicParamInQuery, yangModelCmHandle);
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
- isCmHandleStateReady(yangModelCmHandle, cmHandleState);
- return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ);
+ validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
+ final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null,
+ yangModelCmHandle);
+ final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, resourceId, optionsParamInQuery,
+ topicParamInQuery, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
+ return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ);
+ }
+
+ /**
+ * This method fetches the resource data by data store for given list of cm handles using dmi client.
+ *
+ * @param dataStoreName data store name
+ * @param cmHandleIds list of cm handles
+ * @param resourceId resource identifier
+ * @param optionsParamInQuery options query
+ * @param topicParamInQuery topic name for (triggering) async responses
+ * @param requestId requestId for async responses
+ * @return {@code ResponseEntity} response entity
+ */
+ public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
+ final List<String> cmHandleIds,
+ final String resourceId,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId) {
+ final Collection<YangModelCmHandle> yangModelCmHandles
+ = inventoryPersistence.getYangModelCmHandles(cmHandleIds);
+ final Map<String, Map<String, Map<String, String>>> dmiServiceNameCmHandlePropertiesMap =
+ DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles);
+
+ buildBulkResourceDataRequestAndSend(dataStoreName, resourceId, optionsParamInQuery,
+ topicParamInQuery, requestId, dmiServiceNameCmHandlePropertiesMap);
+ return new ResponseEntity<>(HttpStatus.ACCEPTED);
}
/**
* This method fetches all the resource data from operational data store for given cm handle
* identifier using dmi client.
*
+ * @param dataStoreName data store name
* @param cmHandleId network resource identifier
- * @param dataStore data store enum
* @param requestId requestId for async responses
* @return {@code ResponseEntity} response entity
*/
- public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId,
- final DataStoreEnum dataStore,
+ public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
+ final String cmHandleId,
final String requestId) {
final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
- final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
- final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, "/", null, dataStore,
- null, yangModelCmHandle);
+ final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null,
+ yangModelCmHandle);
+ final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/", null,
+ null, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
- isCmHandleStateReady(yangModelCmHandle, cmHandleState);
- return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ);
+ validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
+ return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody,
+ READ);
}
/**
@@ -121,12 +155,14 @@ public class DmiDataOperations extends DmiOperations {
final String requestData,
final String dataType) {
final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
- final String jsonBody = getDmiRequestBody(operation, null, requestData, dataType, yangModelCmHandle);
- final String dmiUrl = getDmiRequestUrl(cmHandleId, resourceId, null, PASSTHROUGH_RUNNING,
- null, yangModelCmHandle);
+ final String jsonRequestBody = getDmiRequestBody(operation, null, requestData, dataType,
+ yangModelCmHandle);
+ final String dmiUrl = getDmiRequestUrl(PASSTHROUGH_RUNNING.getValue(), cmHandleId, resourceId,
+ null, null,
+ yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
- isCmHandleStateReady(yangModelCmHandle, cmHandleState);
- return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, operation);
+ validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
+ return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operation);
}
private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
@@ -145,24 +181,80 @@ public class DmiDataOperations extends DmiOperations {
return jsonObjectMapper.asJsonString(dmiRequestBody);
}
- private String getDmiRequestUrl(final String cmHandleId,
- final String resourceId,
- final String optionsParamInQuery,
- final DataStoreEnum dataStore,
- final String topicParamInQuery,
- final YangModelCmHandle yangModelCmHandle) {
+ private String getDmiBulkRequestBody(final OperationEnum operation,
+ final String requestId,
+ final String requestData) {
+ final DmiRequestBody dmiBulkRequestBody = DmiRequestBody.builder()
+ .operation(operation)
+ .requestId(requestId)
+ .data(requestData)
+ .build();
+ return jsonObjectMapper.asJsonString(dmiBulkRequestBody);
+ }
+
+ private String getDmiRequestUrl(final String dataStoreName,
+ final String cmHandleId,
+ final String resourceId,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String dmiServiceName) {
return dmiServiceUrlBuilder.getDmiDatastoreUrl(
dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
- topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(
- yangModelCmHandle, cmHandleId, dataStore));
+ topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName,
+ cmHandleId));
}
- private void isCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, final CmHandleState cmHandleState) {
+ private String getDmiServiceBulkRequestUrl(final String dataStoreName,
+ final String resourceId,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String dmiServiceName) {
+ return dmiServiceUrlBuilder.getBulkRequestUrl(
+ dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
+ topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName,
+ NO_CM_HANDLE_ID));
+ }
+
+ private void validateIfCmHandleStateReady(final YangModelCmHandle yangModelCmHandle,
+ final CmHandleState cmHandleState) {
if (cmHandleState != CmHandleState.READY) {
throw new CpsException("State mismatch exception.", "Cm-Handle not in READY state. "
- + "cm handle state is "
- + yangModelCmHandle.getCompositeState().getCmHandleState());
+ + "cm handle state is "
+ + yangModelCmHandle.getCompositeState().getCmHandleState());
}
}
+ private void buildBulkResourceDataRequestAndSend(final String dataStoreName,
+ final String resourceId,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final Map<String, Map<String, Map<String, String>>>
+ dmiServiceNameCmHandlePropertiesMap) {
+ dmiServiceNameCmHandlePropertiesMap.entrySet().parallelStream().forEach(
+ dmiServiceNameCmHandlePropertiesEntry -> {
+ final String dmiBulkResourceDataUrl = getDmiServiceBulkRequestUrl(dataStoreName, resourceId,
+ optionsParamInQuery, topicParamInQuery, dmiServiceNameCmHandlePropertiesEntry.getKey());
+ final String jsonRequestBodyAsJsonString =
+ jsonObjectMapper.asJsonString(dmiServiceNameCmHandlePropertiesEntry.getValue());
+ final String jsonRequestBody
+ = getDmiBulkRequestBody(READ, requestId, jsonRequestBodyAsJsonString);
+ sendDmiResourceDataRequestToDmiService(dmiBulkResourceDataUrl, jsonRequestBody);
+ });
+ }
+
+ private void sendDmiResourceDataRequestToDmiService(final String dmiBulkResourceDataUrl,
+ final String dmiResourceDataRequestAsJsonString) {
+ TaskExecutor.executeTask(() ->
+ dmiRestClient.postOperationWithJsonData(dmiBulkResourceDataUrl,
+ dmiResourceDataRequestAsJsonString, READ),
+ DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS)
+ .whenCompleteAsync(this::handleTaskCompletion);
+ }
+
+ private void handleTaskCompletion(final Object response, final Throwable throwable) {
+ // TODO Need to publish an error response to client given topic.
+ // Code should be implemented into https://jira.onap.org/browse/CPS-1558 (
+ // NCMP : Handle non responding DMI-Plugin)
+ }
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
index d8d03041f..392e9c1a2 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -98,17 +98,18 @@ public class DmiModelOperations extends DmiOperations {
* Get resources from DMI for modules.
*
* @param dmiServiceName dmi service name
- * @param jsonData module names and revisions as JSON
+ * @param jsonRequestBody module names and revisions as JSON
* @param cmHandle cmHandle
* @param resourceName name of the resource(s)
* @return {@code ResponseEntity} response entity
*/
private ResponseEntity<Object> getResourceFromDmiWithJsonData(final String dmiServiceName,
- final String jsonData,
+ final String jsonRequestBody,
final String cmHandle,
final String resourceName) {
final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName);
- return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, DmiRequestBody.OperationEnum.READ);
+ return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody,
+ OperationEnum.READ);
}
private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences,
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java
index e26ffef87..7e9079ec9 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,6 @@
package org.onap.cps.ncmp.api.impl.operations;
-import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
@@ -34,17 +33,6 @@ import org.springframework.stereotype.Service;
@Service
public class DmiOperations {
- @Getter
- public enum DataStoreEnum {
- PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"),
- PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running");
- private final String value;
-
- DataStoreEnum(final String value) {
- this.value = value;
- }
- }
-
protected final InventoryPersistence inventoryPersistence;
protected final JsonObjectMapper jsonObjectMapper;
protected final NcmpConfiguration.DmiProperties dmiProperties;
@@ -52,7 +40,7 @@ public class DmiOperations {
protected final DmiServiceUrlBuilder dmiServiceUrlBuilder;
String getDmiResourceUrl(final String dmiServiceName, final String cmHandle, final String resourceName) {
- return dmiServiceUrlBuilder.getCmHandleUrl()
+ return dmiServiceUrlBuilder.getResourceDataBasePathUriBuilder()
.pathSegment("{resourceName}")
.buildAndExpand(dmiServiceName, dmiProperties.getDmiBasePath(), cmHandle, resourceName).toUriString();
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java
index c84e4cb87..3aa636615 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java
@@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.operations;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonValue;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -34,24 +33,6 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
@Getter
@Builder
public class DmiRequestBody {
- public enum OperationEnum {
- READ("read"),
- CREATE("create"),
- UPDATE("update"),
- PATCH("patch"),
- DELETE("delete");
- private final String value;
-
- OperationEnum(final String value) {
- this.value = value;
- }
-
- @Override
- @JsonValue
- public String toString() {
- return String.valueOf(value);
- }
- }
private OperationEnum operation;
private String dataType;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java
new file mode 100644
index 000000000..796cef23d
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.operations;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum OperationEnum {
+
+ READ("read"),
+ CREATE("create"),
+ UPDATE("update"),
+ PATCH("patch"),
+ DELETE("delete");
+ private final String value;
+
+ OperationEnum(final String value) {
+ this.value = value;
+ }
+
+ @Override
+ @JsonValue
+ public String toString() {
+ return String.valueOf(value);
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java
new file mode 100644
index 000000000..26e94866a
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java
@@ -0,0 +1,64 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.utils;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class DmiServiceNameOrganizer {
+
+ /**
+ * organizes a map with dmi service name as key for cm handle with its properties.
+ *
+ * @param yangModelCmHandles list of cm handle model
+ */
+ public static Map<String, Map<String, Map<String, String>>> getDmiPropertiesPerCmHandleIdPerServiceName(
+ final Collection<YangModelCmHandle> yangModelCmHandles) {
+ final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName
+ = new HashMap<>();
+ yangModelCmHandles.forEach(yangModelCmHandle -> {
+ final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA);
+ if (!dmiPropertiesPerCmHandleIdPerServiceName.containsKey(dmiServiceName)) {
+ final Map<String, Map<String, String>> cmHandleDmiPropertiesMap = new HashMap<>();
+ cmHandleDmiPropertiesMap.put(yangModelCmHandle.getId(),
+ dmiPropertiesAsMap(yangModelCmHandle.getDmiProperties()));
+ dmiPropertiesPerCmHandleIdPerServiceName.put(dmiServiceName, cmHandleDmiPropertiesMap);
+ } else {
+ dmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName)
+ .put(yangModelCmHandle.getId(), dmiPropertiesAsMap(yangModelCmHandle.getDmiProperties()));
+ }
+ });
+ return dmiPropertiesPerCmHandleIdPerServiceName;
+ }
+
+ private static Map<String, String> dmiPropertiesAsMap(final List<YangModelCmHandle.Property> dmiProperties) {
+ return dmiProperties.stream().collect(
+ Collectors.toMap(YangModelCmHandle.Property::getName, YangModelCmHandle.Property::getValue));
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
index 5f4a6540c..bba8f48fb 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,16 +20,12 @@
package org.onap.cps.ncmp.api.impl.utils;
-import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA;
-
import java.util.HashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.apache.logging.log4j.util.TriConsumer;
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
-import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
-import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.spi.utils.CpsValidator;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
@@ -52,13 +48,21 @@ public class DmiServiceUrlBuilder {
*/
public String getDmiDatastoreUrl(final MultiValueMap<String, String> queryParams,
final Map<String, Object> uriVariables) {
- final UriComponentsBuilder uriComponentsBuilder = getCmHandleUrl()
- .pathSegment("data")
- .pathSegment("ds")
- .pathSegment("{dataStore}")
- .queryParams(queryParams)
- .uriVariables(uriVariables);
- return uriComponentsBuilder.buildAndExpand().toUriString();
+ return getUriComponentsBuilder(getResourceDataBasePathUriBuilder(), queryParams, uriVariables)
+ .buildAndExpand().toUriString();
+ }
+
+ /**
+ * This method creates the dmi service url for bulk request.
+ *
+ * @param queryParams query param map as key,value pair
+ * @param uriVariables uri param map as key (placeholder),value pair
+ * @return {@code String} dmi service url as string
+ */
+ public String getBulkRequestUrl(final MultiValueMap<String, String> queryParams,
+ final Map<String, Object> uriVariables) {
+ return getUriComponentsBuilder(getBulkResourceDataBasePathUriBuilder(), queryParams, uriVariables)
+ .buildAndExpand().toUriString();
}
/**
@@ -66,7 +70,7 @@ public class DmiServiceUrlBuilder {
*
* @return {@code UriComponentsBuilder} dmi service url builder object
*/
- public UriComponentsBuilder getCmHandleUrl() {
+ public UriComponentsBuilder getResourceDataBasePathUriBuilder() {
return UriComponentsBuilder.newInstance()
.path("{dmiServiceName}")
.pathSegment("{dmiBasePath}")
@@ -76,23 +80,36 @@ public class DmiServiceUrlBuilder {
}
/**
+ * This method creates the dmi service url builder object with path variables for batch of cm handles.
+ *
+ * @return {@code UriComponentsBuilder} dmi service url builder object
+ */
+ public UriComponentsBuilder getBulkResourceDataBasePathUriBuilder() {
+ return UriComponentsBuilder.newInstance()
+ .path("{dmiServiceName}")
+ .pathSegment("{dmiBasePath}")
+ .pathSegment("v1")
+ .pathSegment("batch");
+ }
+
+ /**
* This method populates uri variables.
*
- * @param yangModelCmHandle get dmi service name
+ * @param dataStoreName data store name
+ * @param dmiServiceName dmi service name
* @param cmHandleId cm handle id for dmi registration
* @return {@code String} dmi service url as string
*/
- public Map<String, Object> populateUriVariables(final YangModelCmHandle yangModelCmHandle,
- final String cmHandleId,
- final DmiOperations.DataStoreEnum dataStore) {
+ public Map<String, Object> populateUriVariables(final String dataStoreName,
+ final String dmiServiceName,
+ final String cmHandleId) {
cpsValidator.validateNameCharacters(cmHandleId);
final Map<String, Object> uriVariables = new HashMap<>();
final String dmiBasePath = dmiProperties.getDmiBasePath();
- uriVariables.put("dmiServiceName",
- yangModelCmHandle.resolveDmiServiceName(DATA));
+ uriVariables.put("dmiServiceName", dmiServiceName);
uriVariables.put("dmiBasePath", dmiBasePath);
uriVariables.put("cmHandleId", cmHandleId);
- uriVariables.put("dataStore", dataStore.getValue());
+ uriVariables.put("dataStore", dataStoreName);
return uriVariables;
}
@@ -124,4 +141,15 @@ public class DmiServiceUrlBuilder {
}
};
}
+
+ private UriComponentsBuilder getUriComponentsBuilder(final UriComponentsBuilder uriComponentsBuilder,
+ final MultiValueMap<String, String> queryParams,
+ final Map<String, Object> uriVariables) {
+ return uriComponentsBuilder
+ .pathSegment("data")
+ .pathSegment("ds")
+ .pathSegment("{dataStore}")
+ .queryParams(queryParams)
+ .uriVariables(uriVariables);
+ }
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
index 537f50122..b9cecfb3d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +21,8 @@
package org.onap.cps.ncmp.api.inventory.sync;
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL;
+
import com.fasterxml.jackson.databind.JsonNode;
import java.time.Duration;
import java.time.OffsetDateTime;
@@ -37,7 +39,6 @@ import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
-import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.inventory.CmHandleQueries;
@@ -167,7 +168,8 @@ public class SyncUtils {
*/
public String getResourceData(final String cmHandleId) {
final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getResourceDataFromDmi(
- cmHandleId, DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
+ PASSTHROUGH_OPERATIONAL.getValue(),
+ cmHandleId,
UUID.randomUUID().toString());
if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) {
return getFirstResource(resourceDataResponseEntity.getBody());
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/SubscriptionEventResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/SubscriptionEventResponse.java
new file mode 100644
index 000000000..95e773c8c
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/SubscriptionEventResponse.java
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.models;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Getter
+@Setter
+public class SubscriptionEventResponse {
+ private String clientId;
+ private String subscriptionName;
+ private String dmiName;
+ private Map<String, SubscriptionStatus> cmHandleIdToStatus;
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/SubscriptionModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/SubscriptionModelLoader.java
index 231ba75b5..659779acf 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/SubscriptionModelLoader.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/SubscriptionModelLoader.java
@@ -31,6 +31,7 @@ import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsModuleService;
import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException;
import org.onap.cps.spi.exceptions.AlreadyDefinedException;
+import org.onap.cps.spi.model.Dataspace;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
@@ -51,6 +52,12 @@ public class SubscriptionModelLoader implements ModelLoader {
private static final String SUBSCRIPTION_SCHEMASET_NAME = "subscriptions";
private static final String SUBSCRIPTION_REGISTRY_DATANODE_NAME = "subscription-registry";
+ @Value("${ncmp.model-loader.maximum-attempt-count:20}")
+ private int maximumAttemptCount;
+
+ @Value("${ncmp.timers.model-loader.retry-time-ms:1000}")
+ private long retryTimeMs;
+
@Value("${ncmp.model-loader.subscription:false}")
private boolean subscriptionModelLoaderEnabled;
@@ -63,6 +70,7 @@ public class SubscriptionModelLoader implements ModelLoader {
public void onApplicationEvent(final ApplicationReadyEvent applicationReadyEvent) {
try {
if (subscriptionModelLoaderEnabled) {
+ checkNcmpDataspaceExists();
onboardSubscriptionModel(createYangResourceToContentMap());
} else {
log.info("Subscription Model Loader is disabled");
@@ -73,6 +81,29 @@ public class SubscriptionModelLoader implements ModelLoader {
}
}
+ private void checkNcmpDataspaceExists() {
+ boolean ncmpDataspaceExists = false;
+ int attemptCount = 0;
+ while (!ncmpDataspaceExists) {
+ final Dataspace ncmpDataspace = cpsAdminService.getDataspace(SUBSCRIPTION_DATASPACE_NAME);
+ if (ncmpDataspace != null) {
+ ncmpDataspaceExists = true;
+ }
+ if (attemptCount < maximumAttemptCount) {
+ try {
+ Thread.sleep(attemptCount * retryTimeMs);
+ attemptCount++;
+ log.info("Retrieving NCMP dataspace... {} attempt(s) ", attemptCount);
+ } catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ } else {
+ throw new NcmpStartUpException("Retrieval of NCMP dataspace fails",
+ "NCMP dataspace does not exist");
+ }
+ }
+ }
+
/**
* Method to onboard subscription model for NCMP.
*/
@@ -108,7 +139,7 @@ public class SubscriptionModelLoader implements ModelLoader {
*/
@Override
public boolean createAnchor(final String dataspaceName, final String schemaSetName,
- final String anchorName) {
+ final String anchorName) {
try {
cpsAdminService.createAnchor(dataspaceName, schemaSetName, anchorName);
} catch (final AlreadyDefinedException exception) {
diff --git a/cps-ncmp-service/src/main/resources/model/subscription.yang b/cps-ncmp-service/src/main/resources/model/subscription.yang
index 8ae1be664..e332a2898 100644
--- a/cps-ncmp-service/src/main/resources/model/subscription.yang
+++ b/cps-ncmp-service/src/main/resources/model/subscription.yang
@@ -4,7 +4,7 @@ module subscription {
prefix subs;
- revision "2023-21-03" {
+ revision "2023-03-21" {
description
"NCMP subscription model";
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
index 871af842e..5b49e0463 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
@@ -52,10 +52,10 @@ import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import spock.lang.Specification
-import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
-import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE
class NetworkCmProxyDataServiceImplSpec extends Specification {
@@ -94,61 +94,64 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def 'Write resource data for pass-through running from DMI using POST.'() {
given: 'cpsDataService returns valid datanode'
- mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
- cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+ mockDataNode()
when: 'write resource data is called'
objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
'testResourceId', CREATE,
'{some-json}', 'application/json')
then: 'DMI called with correct data'
1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
- CREATE, '{some-json}', 'application/json')
+ CREATE, '{some-json}', 'application/json')
>> { new ResponseEntity<>(HttpStatus.CREATED) }
}
def 'Get resource data for pass-through operational from DMI.'() {
given: 'get data node is called'
- mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
- cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+ mockDataNode()
and: 'get resource data from DMI is called'
- mockDmiDataOperations.getResourceDataFromDmi(
- 'testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- PASSTHROUGH_OPERATIONAL,
- NO_REQUEST_ID,
- NO_TOPIC) >> new ResponseEntity<>('dmi-response', HttpStatus.OK)
+ mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.value,'testCmHandle',
+ 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
+ new ResponseEntity<>('dmi-response', HttpStatus.OK)
when: 'get resource data operational for cm-handle is called'
- def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- NO_TOPIC,
- NO_REQUEST_ID)
+ def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.value, 'testCmHandle',
+ 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID)
then: 'DMI returns a json response'
response == 'dmi-response'
}
def 'Get resource data for pass-through running from DMI.'() {
given: 'cpsDataService returns valid data node'
- mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
- cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+ mockDataNode()
and: 'DMI returns valid response and data'
- mockDmiDataOperations.getResourceDataFromDmi('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- PASSTHROUGH_RUNNING,
- NO_REQUEST_ID,
- NO_TOPIC) >> new ResponseEntity<>('{dmi-response}', HttpStatus.OK)
+ mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_RUNNING.value, 'testCmHandle',
+ 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
+ new ResponseEntity<>('{dmi-response}', HttpStatus.OK)
when: 'get resource data is called'
- def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- NO_TOPIC,
- NO_REQUEST_ID)
+ def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.value, 'testCmHandle',
+ 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID)
then: 'get resource data returns expected response'
response == '{dmi-response}'
}
+ def 'Get bulk resource data for #datastoreName from DMI.'() {
+ given: 'cpsDataService returns valid data node'
+ mockDataNode()
+ and: 'DMI returns valid response and data'
+ mockDmiDataOperations.getResourceDataFromDmi(datastoreName, ['testCmHandle'],
+ 'testResourceId', OPTIONS_PARAM,'some topic','requestId') >>
+ new ResponseEntity<>('{dmi-bulk-response}', HttpStatus.OK)
+ when: 'get batch resource data is called'
+ def response = objectUnderTest.getResourceDataForCmHandleBatch(datastoreName, ['testCmHandle'],
+ 'testResourceId',
+ OPTIONS_PARAM,
+ 'some topic',
+ 'requestId')
+ then: 'get bulk resource data returns expected response'
+ response == '{dmi-bulk-response}'
+ where: 'the following data stores are used'
+ datastoreName << [PASSTHROUGH_RUNNING.value, PASSTHROUGH_OPERATIONAL.value]
+ }
+
def 'Getting Yang Resources.'() {
when: 'yang resources is called'
objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
@@ -242,7 +245,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
'{some-json}', 'application/json')
then: 'DMI called with correct data'
1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
- UPDATE, '{some-json}', 'application/json')
+ UPDATE, '{some-json}', 'application/json')
>> { new ResponseEntity<>(HttpStatus.OK) }
}
@@ -365,4 +368,9 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
.dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
.lastSyncTime('some-timestamp').build()).build()
}
+
+ def mockDataNode() {
+ mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
+ cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
index 90839f8ac..b38ca10f7 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,10 +34,9 @@ import org.springframework.web.client.HttpServerErrorException
import org.springframework.web.client.RestTemplate
import spock.lang.Specification
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
-
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
@SpringBootTest
@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiRestClient])
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy
index 868ee7a70..567debdde 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START========================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
package org.onap.cps.ncmp.api.impl.config.embeddedcache
+import com.hazelcast.config.Config
import com.hazelcast.core.Hazelcast
import com.hazelcast.map.IMap
import org.onap.cps.spi.model.DataNode
@@ -50,8 +51,8 @@ class SynchronizationCacheConfigSpec extends Specification {
assert null != moduleSyncStartedOnCmHandles
and: 'system is able to create an instance of a map to hold data sync semaphores'
assert null != dataSyncSemaphores
- and: 'there 3 instances'
- assert Hazelcast.allHazelcastInstances.size() == 3
+ and: 'there are at least 3 instances'
+ assert Hazelcast.allHazelcastInstances.size() > 2
and: 'they have the correct names (in any order)'
assert Hazelcast.allHazelcastInstances.name.containsAll('moduleSyncWorkQueue', 'moduleSyncStartedOnCmHandles', 'dataSyncSemaphores' )
}
@@ -74,6 +75,40 @@ class SynchronizationCacheConfigSpec extends Specification {
assert dataSyncSemaphoresConfig.asyncBackupCount == 3
}
+ def 'Verify deployment network configs for Distributed objects'() {
+ given: 'the Module Sync Work Queue config'
+ def queueNetworkConfig = Hazelcast.getHazelcastInstanceByName('moduleSyncWorkQueue').config.networkConfig
+ and: 'the Module Sync Started Cm Handle Map config'
+ def moduleSyncStartedOnCmHandlesNetworkConfig = Hazelcast.getHazelcastInstanceByName('moduleSyncStartedOnCmHandles').config.networkConfig
+ and: 'the Data Sync Semaphores Map config'
+ def dataSyncSemaphoresNetworkConfig = Hazelcast.getHazelcastInstanceByName('dataSyncSemaphores').config.networkConfig
+ expect: 'system created instance with correct config of Module Sync Work Queue'
+ assert queueNetworkConfig.join.autoDetectionConfig.enabled
+ assert !queueNetworkConfig.join.kubernetesConfig.enabled
+ and: 'Module Sync Started Cm Handle Map has the correct settings'
+ assert moduleSyncStartedOnCmHandlesNetworkConfig.join.autoDetectionConfig.enabled
+ assert !moduleSyncStartedOnCmHandlesNetworkConfig.join.kubernetesConfig.enabled
+ and: 'Data Sync Semaphore Map has the correct settings'
+ assert dataSyncSemaphoresNetworkConfig.join.autoDetectionConfig.enabled
+ assert !dataSyncSemaphoresNetworkConfig.join.kubernetesConfig.enabled
+
+ }
+
+ def 'Verify network config'() {
+ given: 'Synchronization config object and test configuration'
+ def objectUnderTest = new SynchronizationCacheConfig()
+ def testConfig = new Config()
+ when: 'kubernetes properties are enabled'
+ objectUnderTest.cacheKubernetesEnabled = true
+ objectUnderTest.cacheKubernetesServiceName = 'test-service-name'
+ and: 'method called to update the discovery mode'
+ objectUnderTest.updateDiscoveryMode(testConfig)
+ then: 'applied properties are reflected'
+ assert testConfig.networkConfig.join.kubernetesConfig.enabled
+ assert testConfig.networkConfig.join.kubernetesConfig.properties.get('service-name') == 'test-service-name'
+
+ }
+
def 'Time to Live Verify for Module Sync Semaphore'() {
when: 'the key is inserted with a TTL of 1 second (Hazelcast TTL resolution is seconds!)'
moduleSyncStartedOnCmHandles.put('testKeyModuleSync', 'toBeExpired' as Object, 1, TimeUnit.SECONDS)
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfigSpec.groovy
new file mode 100644
index 000000000..7448daf59
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfigSpec.groovy
@@ -0,0 +1,75 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.event.avc
+
+import com.hazelcast.config.Config
+import com.hazelcast.core.Hazelcast
+import com.hazelcast.map.IMap
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import spock.lang.Specification
+
+@SpringBootTest(classes = [ForwardedSubscriptionEventCacheConfig])
+class ForwardedSubscriptionEventCacheConfigSpec extends Specification {
+
+ @Autowired
+ private IMap<String, Set<String>> forwardedSubscriptionEventCache
+
+ def 'Embedded (hazelcast) cache for Forwarded Subscription Event Cache.'() {
+ expect: 'system is able to create an instance of the Forwarded Subscription Event Cache'
+ assert null != forwardedSubscriptionEventCache
+ and: 'there is at least 1 instance'
+ assert Hazelcast.allHazelcastInstances.size() > 0
+ and: 'Forwarded Subscription Event Cache is present'
+ assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceSubscriptionEvents')
+ }
+
+ def 'Verify configs for Distributed Caches'(){
+ given: 'the Forwarded Subscription Event Cache config'
+ def forwardedSubscriptionEventCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelCastInstanceSubscriptionEvents').config.mapConfigs.get('forwardedSubscriptionEventCacheMapConfig')
+ expect: 'system created instance with correct config'
+ assert forwardedSubscriptionEventCacheConfig.backupCount == 3
+ assert forwardedSubscriptionEventCacheConfig.asyncBackupCount == 3
+ }
+
+ def 'Verify deployment network configs for Distributed Caches'() {
+ given: 'the Forwarded Subscription Event Cache config'
+ def forwardedSubscriptionEventCacheNetworkConfig = Hazelcast.getHazelcastInstanceByName('hazelCastInstanceSubscriptionEvents').config.networkConfig
+ expect: 'system created instance with correct config'
+ assert forwardedSubscriptionEventCacheNetworkConfig.join.autoDetectionConfig.enabled
+ assert !forwardedSubscriptionEventCacheNetworkConfig.join.kubernetesConfig.enabled
+ }
+
+ def 'Verify network config'() {
+ given: 'Synchronization config object and test configuration'
+ def objectUnderTest = new ForwardedSubscriptionEventCacheConfig()
+ def testConfig = new Config()
+ when: 'kubernetes properties are enabled'
+ objectUnderTest.cacheKubernetesEnabled = true
+ objectUnderTest.cacheKubernetesServiceName = 'test-service-name'
+ and: 'method called to update the discovery mode'
+ objectUnderTest.updateDiscoveryMode(testConfig)
+ then: 'applied properties are reflected'
+ assert testConfig.networkConfig.join.kubernetesConfig.enabled
+ assert testConfig.networkConfig.join.kubernetesConfig.properties.get('service-name') == 'test-service-name'
+
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumerSpec.groovy
new file mode 100644
index 000000000..a67346200
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumerSpec.groovy
@@ -0,0 +1,79 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2023 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.event.avc
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.hazelcast.map.IMap
+import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
+import org.onap.cps.ncmp.api.models.SubscriptionEventResponse
+import org.onap.cps.utils.JsonObjectMapper
+import org.springframework.boot.test.context.SpringBootTest
+
+@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper])
+class SubscriptionEventResponseConsumerSpec extends MessagingBaseSpec {
+
+ IMap<String, Set<String>> mockForwardedSubscriptionEventCache = Mock(IMap<String, Set<String>>)
+
+ def objectUnderTest = new SubscriptionEventResponseConsumer(mockForwardedSubscriptionEventCache)
+
+
+ def 'Consume Subscription Event Response where all DMIs have responded'() {
+ given: 'a subscription event response with a clientId, subscriptionName and dmiName'
+ def testEventReceived = new SubscriptionEventResponse()
+ testEventReceived.clientId = 'some-client-id'
+ testEventReceived.subscriptionName = 'some-subscription-name'
+ testEventReceived.dmiName = 'some-dmi-name'
+ and: 'notifications are enabled'
+ objectUnderTest.notificationFeatureEnabled = true
+ and: 'subscription model loader is enabled'
+ objectUnderTest.subscriptionModelLoaderEnabled = true
+ when: 'the valid event is consumed'
+ objectUnderTest.consumeSubscriptionEventResponse(testEventReceived)
+ then: 'the forwarded subscription event cache returns only the received dmiName existing for the subscription create event'
+ 1 * mockForwardedSubscriptionEventCache.containsKey('some-client-idsome-subscription-name') >> true
+ 1 * mockForwardedSubscriptionEventCache.get('some-client-idsome-subscription-name') >> (['some-dmi-name'] as Set)
+ and: 'the forwarded subscription event cache returns an empty Map when the dmiName has been removed'
+ 1 * mockForwardedSubscriptionEventCache.get('some-client-idsome-subscription-name') >> ([] as Set)
+ and: 'the subscription event is removed from the map'
+ 1 * mockForwardedSubscriptionEventCache.remove('some-client-idsome-subscription-name')
+ }
+
+ def 'Consume Subscription Event Response where another DMI has not yet responded'() {
+ given: 'a subscription event response with a clientId, subscriptionName and dmiName'
+ def testEventReceived = new SubscriptionEventResponse()
+ testEventReceived.clientId = 'some-client-id'
+ testEventReceived.subscriptionName = 'some-subscription-name'
+ testEventReceived.dmiName = 'some-dmi-name'
+ and: 'notifications are enabled'
+ objectUnderTest.notificationFeatureEnabled = true
+ and: 'subscription model loader is enabled'
+ objectUnderTest.subscriptionModelLoaderEnabled = true
+ when: 'the valid event is consumed'
+ objectUnderTest.consumeSubscriptionEventResponse(testEventReceived)
+ then: 'the forwarded subscription event cache returns only the received dmiName existing for the subscription create event'
+ 1 * mockForwardedSubscriptionEventCache.containsKey('some-client-idsome-subscription-name') >> true
+ 1 * mockForwardedSubscriptionEventCache.get('some-client-idsome-subscription-name') >> (['some-dmi-name', 'non-responded-dmi'] as Set)
+ and: 'the forwarded subscription event cache returns an empty Map when the dmiName has been removed'
+ 1 * mockForwardedSubscriptionEventCache.get('some-client-idsome-subscription-name') >> (['non-responded-dmi'] as Set)
+ and: 'the subscription event is not removed from the map'
+ 0 * mockForwardedSubscriptionEventCache.remove(_)
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/SubscriptionEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/SubscriptionEventMapperSpec.groovy
index 3a7aa481c..f2ff1f7b2 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/SubscriptionEventMapperSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/SubscriptionEventMapperSpec.groovy
@@ -48,7 +48,7 @@ class SubscriptionEventMapperSpec extends Specification {
def result = objectUnderTest.toYangModelSubscriptionEvent(testEventToMap)
then: 'the resulting yang model subscription event contains the correct clientId'
assert result.clientId == "SCO-9989752"
- and: 'client name'
+ and: 'subscription name'
assert result.subscriptionName == "cm-subscription-001"
and: 'is tagged value is false'
assert !result.isTagged
@@ -60,4 +60,21 @@ class SubscriptionEventMapperSpec extends Specification {
assert result.topic == null
}
+ def 'Map null subscription event to yang model subscription event where #scenario'() {
+ given: 'a new Subscription Event with no data'
+ def testEventToMap = new SubscriptionEvent()
+ when: 'the event is mapped to a yang model subscription'
+ def result = objectUnderTest.toYangModelSubscriptionEvent(testEventToMap)
+ then: 'the resulting yang model subscription event contains null clientId'
+ assert result.clientId == null
+ and: 'subscription name is null'
+ assert result.subscriptionName == null
+ and: 'is tagged value is false'
+ assert result.isTagged == false
+ and: 'predicates is null'
+ assert result.predicates == null
+ and: 'the topic is null'
+ assert result.topic == null
+ }
+
} \ No newline at end of file
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarderSpec.groovy
index 2b0adf342..457eb6fa9 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarderSpec.groovy
@@ -21,6 +21,7 @@
package org.onap.cps.ncmp.api.impl.events.avcsubscription
import com.fasterxml.jackson.databind.ObjectMapper
+import com.hazelcast.map.IMap
import org.onap.cps.ncmp.api.impl.events.EventsPublisher
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.inventory.InventoryPersistence
@@ -29,20 +30,28 @@ import org.onap.cps.ncmp.event.model.SubscriptionEvent
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.spi.exceptions.OperationNotYetSupportedException
import org.onap.cps.utils.JsonObjectMapper
+import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
+import spock.util.concurrent.BlockingVariable
-@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper])
+@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper, SubscriptionEventForwarder])
class SubscriptionEventForwarderSpec extends MessagingBaseSpec {
- def mockInventoryPersistence = Mock(InventoryPersistence)
- def mockSubscriptionEventPublisher = Mock(EventsPublisher<SubscriptionEvent>)
- def objectUnderTest = new SubscriptionEventForwarder(mockInventoryPersistence, mockSubscriptionEventPublisher)
+ @Autowired
+ SubscriptionEventForwarder objectUnderTest
+
+ @SpringBean
+ InventoryPersistence mockInventoryPersistence = Mock(InventoryPersistence)
+ @SpringBean
+ EventsPublisher<SubscriptionEvent> mockSubscriptionEventPublisher = Mock(EventsPublisher<SubscriptionEvent>)
+ @SpringBean
+ IMap<String, Set<String>> mockForwardedSubscriptionEventCache = Mock(IMap<String, Set<String>>)
@Autowired
JsonObjectMapper jsonObjectMapper
- def 'Forward valid CM create subscription'() {
+ def 'Forward valid CM create subscription and simulate timeout where #scenario'() {
given: 'an event'
def jsonData = TestUtils.getResourceFileContent('avcSubscriptionCreationEvent.json')
def testEventSent = jsonObjectMapper.convertJsonString(jsonData, SubscriptionEvent.class)
@@ -52,9 +61,17 @@ class SubscriptionEventForwarderSpec extends MessagingBaseSpec {
createYangModelCmHandleWithDmiProperty(2, 1,"shape","square"),
createYangModelCmHandleWithDmiProperty(3, 2,"shape","triangle")
]
+ and: 'the thread creation delay is reduced to 2 seconds for testing'
+ objectUnderTest.dmiResponseTimeoutInMs = 2000
+ and: 'a Blocking Variable is used for the Asynchronous call with a timeout of 5 seconds'
+ def block = new BlockingVariable<Object>(5)
when: 'the valid event is forwarded'
objectUnderTest.forwardCreateSubscriptionEvent(testEventSent)
- then: 'the event is forwarded twice with the CMHandle private properties and provides a valid listenable future'
+ then: 'An asynchronous call is made to the blocking variable'
+ block.get()
+ then: 'the event is added to the forwarded subscription event cache'
+ 1 * mockForwardedSubscriptionEventCache.put("SCO-9989752cm-subscription-001", ["DMIName1", "DMIName2"] as Set)
+ and: 'the event is forwarded twice with the CMHandle private properties and provides a valid listenable future'
1 * mockSubscriptionEventPublisher.publishEvent("ncmp-dmi-cm-avc-subscription-DMIName1", "SCO-9989752-cm-subscription-001-DMIName1",
subscriptionEvent -> {
Map targets = subscriptionEvent.getEvent().getPredicates().getTargets().get(0)
@@ -68,6 +85,15 @@ class SubscriptionEventForwarderSpec extends MessagingBaseSpec {
targets["CMHandle3"] == ["shape":"triangle"]
}
)
+ and: 'a separate thread has been created where the map is polled'
+ 1 * mockForwardedSubscriptionEventCache.containsKey("SCO-9989752cm-subscription-001") >> true
+ 1 * mockForwardedSubscriptionEventCache.get(_) >> (DMINamesInMap)
+ and: 'the subscription id is removed from the event cache map returning the asynchronous blocking variable'
+ 1 * mockForwardedSubscriptionEventCache.remove("SCO-9989752cm-subscription-001") >> {block.set(_)}
+ where:
+ scenario | DMINamesInMap
+ 'there are dmis which have not responded' | ["DMIName1", "DMIName2"] as Set
+ 'all dmis have responded ' | [] as Set
}
def 'Forward CM create subscription where target CM Handles are #scenario'() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
index 03825c2bb..89b3a2ff2 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,14 +30,14 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.ResponseEntity
import org.springframework.test.context.ContextConfiguration
+import org.springframework.http.HttpStatus
import spock.lang.Shared
-import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
-import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
-import org.springframework.http.HttpStatus
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE
@SpringBootTest
@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiDataOperations])
@@ -50,6 +50,8 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
def NO_REQUEST_ID = null
@Shared
def OPTIONS_PARAM = '(a=1,b=2)'
+ @Shared
+ def expectedBulkRequestAsJson = '{"operation": "read","data": {"fe1c1f1a070c4ce5bbfda7198592a0d3": {"neType": "RadioNode"},"b8e42eed0d9541ed8d8839e8eb86b3e0": {"neType": "RadioNode"}},"requestId": "bbb67474-f705-410a-93d1-b2844e7f45fd"}'
@SpringBean
JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
@@ -66,8 +68,8 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
mockDmiRestClient.postOperationWithJsonData(expectedUrl, expectedJson, READ) >> responseFromDmi
dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
when: 'get resource data is invoked'
- def result = objectUnderTest.getResourceDataFromDmi(cmHandleId, resourceIdentifier,
- options, dataStore, NO_REQUEST_ID, NO_TOPIC)
+ def result = objectUnderTest.getResourceDataFromDmi(dataStore.value, cmHandleId, resourceIdentifier,
+ options, NO_TOPIC, NO_REQUEST_ID)
then: 'the result is the response from the DMI service'
assert result == responseFromDmi
where: 'the following parameters are used'
@@ -80,6 +82,25 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
'datastore running with properties' | [yangModelCmHandleProperty] | PASSTHROUGH_RUNNING | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-running' | '&options=(a=1,b=2)'
}
+ def 'call get bulk resource data for #dataStore from DMI service with topic #scenario.'() {
+ given: 'collection of yang model cm Handles'
+ mockYangModelCmHandleCollectionRetrieval([yangModelCmHandleProperty])
+ and: 'a positive response from DMI service when it is called with the expected parameters'
+ def responseFromDmi = new ResponseEntity<Object>(HttpStatus.ACCEPTED)
+ def expectedDmiBulkResourceDataUrl = "ncmp/v1/batch/data/ds/${dataStore}?resourceIdentifier=parent/child%26options=(a=1,b=2)&topic=my-topic-name&options=(fields=schemas/schema)"
+ mockDmiRestClient.postOperationWithJsonData(expectedDmiBulkResourceDataUrl, expectedBulkRequestAsJson, READ) >> responseFromDmi
+ dmiServiceUrlBuilder.getBulkRequestUrl(_, _) >> expectedDmiBulkResourceDataUrl
+ when: 'get resource data for bulk cm handle is invoked'
+ def result = objectUnderTest.getResourceDataFromDmi( dataStore.value, [cmHandleId], resourceIdentifier,
+ OPTIONS_PARAM, 'some-topic','requestId')
+ then: 'the result is the response from the DMI service'
+ assert result == responseFromDmi
+ where: 'the following parameters are used'
+ scenario | dataStore
+ 'datastore operational' | PASSTHROUGH_OPERATIONAL
+ 'datastore running' | PASSTHROUGH_RUNNING
+ }
+
def 'call get all resource data.'() {
given: 'the system returns a cm handle with a sample property'
mockYangModelCmHandleRetrieval([yangModelCmHandleProperty])
@@ -89,7 +110,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
mockDmiRestClient.postOperationWithJsonData(expectedUrl, '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}', READ) >> responseFromDmi
dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
when: 'get resource data is invoked'
- def result = objectUnderTest.getResourceDataFromDmi(cmHandleId, PASSTHROUGH_OPERATIONAL, NO_REQUEST_ID)
+ def result = objectUnderTest.getResourceDataFromDmi( PASSTHROUGH_OPERATIONAL.value, cmHandleId, NO_REQUEST_ID)
then: 'the result is the response from the DMI service'
assert result == responseFromDmi
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
index ed8f08698..ed74ad334 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,6 +24,8 @@ package org.onap.cps.ncmp.api.impl.operations
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
+import org.onap.cps.ncmp.api.impl.executor.TaskExecutor
+import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
@@ -34,7 +36,7 @@ import org.springframework.http.ResponseEntity
import org.springframework.test.context.ContextConfiguration
import spock.lang.Shared
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ
@SpringBootTest
@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiModelOperations])
@@ -49,6 +51,12 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
@SpringBean
JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
+ @SpringBean
+ TaskExecutor stubbedTaskExecutor = Stub()
+
+ @SpringBean
+ DmiServiceNameOrganizer stubbedDmiServiceNameOrganizer = Stub()
+
def 'Retrieving module references.'() {
given: 'a cm handle'
mockYangModelCmHandleRetrieval([])
@@ -89,7 +97,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
and: 'a positive response from DMI service when it is called with tha expected parameters'
def responseFromDmi = new ResponseEntity<String>(HttpStatus.OK)
mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules",
- '{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi
+ '{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi
when: 'a get module references is called'
def result = objectUnderTest.getModuleReferences(yangModelCmHandle)
then: 'the result is the response from DMI service'
@@ -108,7 +116,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
[moduleName: 'mod2', revision: 'C', yangSource: 'other yang source']], HttpStatus.OK)
def expectedModuleReferencesInRequest = '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
- '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', READ) >> responseFromDmi
+ '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', READ) >> responseFromDmi
when: 'get new yang resources from DMI service'
def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, newModuleReferences)
then: 'the result has the 2 expected yang (re)sources (order is not guaranteed)'
@@ -140,7 +148,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
and: 'a positive response from DMI service when it is called with the expected parameters'
def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK)
mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
- '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi
+ '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi
when: 'get new yang resources from DMI service'
def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, unknownModuleReferences)
then: 'the result is the response from DMI service'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy
index c4d0020a6..1b2c50ae7 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy
@@ -58,12 +58,21 @@ abstract class DmiOperationsBaseSpec extends Specification {
def static resourceIdentifier = 'parent/child'
def mockYangModelCmHandleRetrieval(dmiProperties) {
+ populateYangModelCmHandle(dmiProperties)
+ mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle
+ }
+
+ def mockYangModelCmHandleCollectionRetrieval(dmiProperties) {
+ populateYangModelCmHandle(dmiProperties)
+ mockInventoryPersistence.getYangModelCmHandles(_) >> [yangModelCmHandle]
+ }
+
+ def populateYangModelCmHandle(dmiProperties) {
yangModelCmHandle.dmiDataServiceName = dmiServiceName
yangModelCmHandle.dmiServiceName = dmiServiceName
yangModelCmHandle.dmiProperties = dmiProperties
yangModelCmHandle.id = cmHandleId
yangModelCmHandle.compositeState = new CompositeState()
yangModelCmHandle.compositeState.cmHandleState = CmHandleState.READY
- mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy
index 01569887c..6ca310550 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,9 +20,10 @@
package org.onap.cps.ncmp.api.impl.utils
+import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService
import org.onap.cps.spi.utils.CpsValidator
-import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
@@ -45,8 +46,8 @@ class DmiServiceUrlBuilderSpec extends Specification {
def 'Create the dmi service url with #scenario.'() {
given: 'uri variables'
dmiProperties.dmiBasePath = 'dmi'
- def uriVars = objectUnderTest.populateUriVariables(yangModelCmHandle,
- "cmHandle", PASSTHROUGH_RUNNING)
+ def uriVars = objectUnderTest.populateUriVariables(PASSTHROUGH_RUNNING.value, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA),
+ "cmHandle")
and: 'query params'
def uriQueries = objectUnderTest.populateQueryParams(resourceId,
'optionsParamInQuery', topic)
@@ -65,8 +66,8 @@ class DmiServiceUrlBuilderSpec extends Specification {
def 'Populate dmi data store url #scenario.'() {
given: 'uri variables are created'
dmiProperties.dmiBasePath = dmiBasePath
- def uriVars = objectUnderTest.populateUriVariables(yangModelCmHandle,
- "cmHandle", PASSTHROUGH_RUNNING)
+ def uriVars = objectUnderTest.populateUriVariables(PASSTHROUGH_RUNNING.value, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA),
+ "cmHandle")
and: 'null query params'
def uriQueries = objectUnderTest.populateQueryParams(null,
null, null)
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
index f4176d621..8164dcf9c 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,10 +21,11 @@
package org.onap.cps.ncmp.api.inventory.sync
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
+
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
-import org.onap.cps.ncmp.api.impl.operations.DmiOperations
import org.onap.cps.ncmp.api.inventory.CmHandleQueries
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
@@ -38,7 +39,6 @@ import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import spock.lang.Shared
import spock.lang.Specification
-
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import java.util.stream.Collectors
@@ -137,7 +137,7 @@ class SyncUtilsSpec extends Specification{
def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString);
def responseEntity = new ResponseEntity<>(jsonNode, HttpStatus.OK)
- mockDmiDataOperations.getResourceDataFromDmi('cm-handle-123', DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, _) >> responseEntity
+ mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.value, 'cm-handle-123', _) >> responseEntity
when: 'get resource data is called'
def result = objectUnderTest.getResourceData('cm-handle-123')
then: 'the returned data is correct'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/SubscriptionModelLoaderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/SubscriptionModelLoaderSpec.groovy
index aa8bc53c9..a14a0f286 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/SubscriptionModelLoaderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/SubscriptionModelLoaderSpec.groovy
@@ -32,6 +32,7 @@ import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException
import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
+import org.onap.cps.spi.model.Dataspace
import org.springframework.boot.SpringApplication
import org.slf4j.LoggerFactory
import org.springframework.boot.context.event.ApplicationReadyEvent
@@ -73,9 +74,15 @@ class SubscriptionModelLoaderSpec extends Specification {
}
def 'Onboard subscription model successfully via application ready event'() {
- when:'model loader is enabled'
+ given: 'dataspace is ready for use'
+ mockCpsAdminService.getDataspace('NCMP-Admin') >> new Dataspace('NCMP-Admin')
+ and:'model loader is enabled'
objectUnderTest.subscriptionModelLoaderEnabled = true
- and: 'the application is ready'
+ and: 'maximum attempt count is set'
+ objectUnderTest.maximumAttemptCount = 20
+ and: 'retry time is set'
+ objectUnderTest.retryTimeMs = 100
+ when: 'the application is ready'
objectUnderTest.onApplicationEvent(applicationReadyEvent)
then: 'the module service to create schema set is called once'
1 * mockCpsModuleService.createSchemaSet('NCMP-Admin', 'subscriptions',sampleYangContentMap)
@@ -90,6 +97,8 @@ class SubscriptionModelLoaderSpec extends Specification {
objectUnderTest.subscriptionModelLoaderEnabled = false
and: 'application is ready'
objectUnderTest.onApplicationEvent(applicationReadyEvent)
+ and: 'dataspace is ready for use'
+ mockCpsAdminService.getDataspace('NCMP-Admin') >> new Dataspace('NCMP-Admin')
then: 'the module service to create schema set was not called'
0 * mockCpsModuleService.createSchemaSet(*_)
and: 'the admin service to create an anchor set was not called'
@@ -98,11 +107,34 @@ class SubscriptionModelLoaderSpec extends Specification {
0 * mockCpsDataService.saveData(*_)
}
+ def 'Onboard subscription model fails as NCMP dataspace does not exist' () {
+ given: 'model loader is enabled'
+ objectUnderTest.subscriptionModelLoaderEnabled = true
+ and: 'maximum attempt count is set'
+ objectUnderTest.maximumAttemptCount = 20
+ and: 'retry time is set'
+ objectUnderTest.retryTimeMs = 100
+ when: 'the application is ready'
+ objectUnderTest.onApplicationEvent(applicationReadyEvent)
+ then: 'the module service to create schema set was not called'
+ 0 * mockCpsModuleService.createSchemaSet(*_)
+ and: 'the admin service to create an anchor set was not called'
+ 0 * mockCpsAdminService.createAnchor(*_)
+ and: 'the data service to create a top level datanode was not called'
+ 0 * mockCpsDataService.saveData(*_)
+ and: 'the log message contains the correct exception message'
+ def logs = appender.list.toString()
+ assert logs.contains("Retrieval of NCMP dataspace fails")
+ }
+
+
def 'Exception occurred while schema set creation' () {
given: 'creating a schema set throws an exception'
mockCpsModuleService.createSchemaSet(*_) >> { throw new DataValidationException(*_) }
and: 'model loader is enabled'
objectUnderTest.subscriptionModelLoaderEnabled = true
+ and: 'dataspace is ready for use'
+ mockCpsAdminService.getDataspace('NCMP-Admin') >> new Dataspace('NCMP-Admin')
when: 'application is ready'
objectUnderTest.onApplicationEvent(applicationReadyEvent)
then: 'the admin service to create an anchor set was not called'
diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml
index e66f23d23..679248ba8 100644
--- a/cps-ncmp-service/src/test/resources/application.yml
+++ b/cps-ncmp-service/src/test/resources/application.yml
@@ -35,4 +35,11 @@ ncmp:
parallelism-level: 3
model-loader:
- subscription: true \ No newline at end of file
+ subscription: true
+
+# Custom Hazelcast Config.
+hazelcast:
+ mode:
+ kubernetes:
+ enabled: false
+ service-name: "cps-and-ncmp-service" \ No newline at end of file
diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java
index 854450c8b..f44e310a1 100644
--- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java
+++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java
@@ -24,7 +24,7 @@ package org.onap.cps.cpspath.parser;
import static org.onap.cps.cpspath.parser.CpsPathPrefixType.DESCENDANT;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -47,7 +47,7 @@ public class CpsPathBuilder extends CpsPathBaseListener {
final CpsPathQuery cpsPathQuery = new CpsPathQuery();
- final Map<String, Object> leavesData = new HashMap<>();
+ final Map<String, Object> leavesData = new LinkedHashMap<>();
final StringBuilder normalizedXpathBuilder = new StringBuilder();
diff --git a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
index 96fdf88cf..9552a4d34 100644
--- a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
+++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
@@ -84,6 +84,8 @@ class CpsPathQuerySpec extends Specification {
'parent & child with more than one attribute has OR operator' | '/parent/child[@key1=1 or @key2="abc"]/child2' || "/parent/child[@key1='1' or @key2='abc']/child2"
'parent & child with multiple AND operators' | '/parent/child[@key1=1 and @key2="abc" and @key="xyz"]/child2' || "/parent/child[@key1='1' and @key2='abc' and @key='xyz']/child2"
'parent & child with multiple OR operators' | '/parent/child[@key1=1 or @key2="abc" or @key="xyz"]/child2' || "/parent/child[@key1='1' or @key2='abc' or @key='xyz']/child2"
+ 'parent & child with multiple AND/OR combination' | '/parent/child[@key1=1 and @key2="abc" or @key="xyz"]/child2' || "/parent/child[@key1='1' and @key2='abc' or @key='xyz']/child2"
+ 'parent & child with multiple OR/AND combination' | '/parent/child[@key1=1 or @key2="abc" and @key="xyz"]/child2' || "/parent/child[@key1='1' or @key2='abc' and @key='xyz']/child2"
}
def 'Parse xpath to form the Normalized xpath containing #scenario.'() {
@@ -105,14 +107,17 @@ class CpsPathQuerySpec extends Specification {
and: 'the right parameters are set'
result.descendantName == "child"
result.leavesData.size() == expectedNumberOfLeaves
+ and: 'the given operator(s) returns in the correct order'
result.booleanOperatorsType == expectedOperators
where: 'the following data is used'
- scenario | cpsPath || expectedNumberOfLeaves || expectedOperators
- 'one attribute' | '//child[@common-leaf-name-int=5]' || 1 || null
- 'more than one attribute has AND operator' | '//child[@int-leaf=5 and @leaf-name="leaf value"]' || 2 || ['and']
- 'more than one attribute has OR operator' | '//child[@int-leaf=5 or @leaf-name="leaf value"]' || 2 || ['or']
- 'more than one attribute has combinations and/or operator' | '//child[@int-leaf=5 and @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2 || ['and', 'and']
- 'more than one attribute has combinations or/and operator' | '//child[@int-leaf=5 or @leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 2 || ['or', 'or']
+ scenario | cpsPath || expectedNumberOfLeaves || expectedOperators
+ 'one attribute' | '//child[@common-leaf-name-int=5]' || 1 || null
+ 'more than one attribute has AND operator' | '//child[@int-leaf=5 and @leaf-name="leaf value"]' || 2 || ['and']
+ 'more than one attribute has OR operator' | '//child[@int-leaf=5 or @leaf-name="leaf value"]' || 2 || ['or']
+ 'more than one attribute has combinations and operators' | '//child[@int-leaf=5 and @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2 || ['and', 'and']
+ 'more than one attribute has combinations or operators' | '//child[@int-leaf=5 or @leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 2 || ['or', 'or']
+ 'more than one attribute has combinations and/or combination' | '//child[@int-leaf=5 and @leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 2 || ['and', 'or']
+ 'more than one attribute has combinations or/and combination' | '//child[@int-leaf=5 or @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2 || ['or', 'and']
}
def 'Parse #scenario cps path with text function condition'() {
@@ -136,7 +141,7 @@ class CpsPathQuerySpec extends Specification {
'descendant with leaf value and ancestor' | '//child[@other-leaf=1]/leaf-name[text()="search"]/ancestor::parent' || true | true
}
- def 'Parse #scenario cps path with contains function condition'() {
+ def 'Parse cps path with contains function condition'() {
when: 'the given cps path is parsed'
def result = CpsPathQuery.createFrom('//someContainer[contains(@lang,"en")]')
then: 'the query has the right xpath type'
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
index 369e5289b..3d9105f68 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
@@ -494,21 +494,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
}
@Override
- public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName,
- final DataNode dataNode) {
- final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
- final FragmentEntity fragmentEntity = getFragmentEntity(anchorEntity, dataNode.getXpath());
- updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode);
- try {
- fragmentRepository.save(fragmentEntity);
- } catch (final StaleStateException staleStateException) {
- throw new ConcurrencyException("Concurrent Transactions",
- String.format("dataspace :'%s', Anchor : '%s' and xpath: '%s' is updated by another transaction.",
- dataspaceName, anchorName, dataNode.getXpath()));
- }
- }
-
- @Override
public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
final Collection<DataNode> updatedDataNodes) {
final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
index b4366de75..cd1457e35 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
@@ -159,7 +159,7 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
@Override
public Collection<SchemaSet> getSchemaSetsByDataspaceName(final String dataspaceName) {
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
- final List<SchemaSetEntity> schemaSetEntities = schemaSetRepository.getByDataspace(dataspaceEntity);
+ final List<SchemaSetEntity> schemaSetEntities = schemaSetRepository.findByDataspace(dataspaceEntity);
return schemaSetEntities.stream()
.map(CpsModulePersistenceServiceImpl::toSchemaSet).collect(Collectors.toList());
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
index f7b586d7b..fe9ff9e2f 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
@@ -27,6 +27,7 @@ import org.onap.cps.spi.entities.DataspaceEntity;
import org.onap.cps.spi.entities.SchemaSetEntity;
import org.onap.cps.spi.exceptions.AnchorNotFoundException;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@@ -45,11 +46,27 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Integer> {
Collection<AnchorEntity> findAllBySchemaSet(SchemaSetEntity schemaSetEntity);
- Collection<AnchorEntity> findAllByDataspaceAndNameIn(DataspaceEntity dataspaceEntity,
- Collection<String> anchorNames);
+ @Query(value = "SELECT * FROM anchor WHERE dataspace_id = :dataspaceId AND name = ANY (:anchorNames)",
+ nativeQuery = true)
+ Collection<AnchorEntity> findAllByDataspaceIdAndNameIn(@Param("dataspaceId") int dataspaceId,
+ @Param("anchorNames") String[] anchorNames);
- Collection<AnchorEntity> findAllByDataspaceAndSchemaSetNameIn(DataspaceEntity dataspaceEntity,
- Collection<String> schemaSetNames);
+ default Collection<AnchorEntity> findAllByDataspaceAndNameIn(final DataspaceEntity dataspaceEntity,
+ final Collection<String> anchorNames) {
+ return findAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames.toArray(new String[0]));
+ }
+
+ @Query(value = "SELECT a.* FROM anchor a"
+ + " LEFT OUTER JOIN schema_set s ON a.schema_set_id = s.id"
+ + " WHERE a.dataspace_id = :dataspaceId AND s.name = ANY (:schemaSetNames)",
+ nativeQuery = true)
+ Collection<AnchorEntity> findAllByDataspaceIdAndSchemaSetNameIn(@Param("dataspaceId") int dataspaceId,
+ @Param("schemaSetNames") String[] schemaSetNames);
+
+ default Collection<AnchorEntity> findAllByDataspaceAndSchemaSetNameIn(final DataspaceEntity dataspaceEntity,
+ final Collection<String> schemaSetNames) {
+ return findAllByDataspaceIdAndSchemaSetNameIn(dataspaceEntity.getId(), schemaSetNames.toArray(new String[0]));
+ }
Integer countByDataspace(DataspaceEntity dataspaceEntity);
@@ -57,12 +74,29 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Integer> {
+ "JOIN schema_set_yang_resources ON schema_set_yang_resources.yang_resource_id = yang_resource.id\n"
+ "JOIN schema_set ON schema_set.id = schema_set_yang_resources.schema_set_id\n"
+ "JOIN anchor ON anchor.schema_set_id = schema_set.id\n"
- + "WHERE schema_set.dataspace_id = :dataspaceId AND module_name IN (:moduleNames)\n"
+ + "WHERE schema_set.dataspace_id = :dataspaceId AND module_name = ANY (:moduleNames)\n"
+ "GROUP BY anchor.id, anchor.name, anchor.dataspace_id, anchor.schema_set_id\n"
+ "HAVING COUNT(DISTINCT module_name) = :sizeOfModuleNames", nativeQuery = true)
Collection<AnchorEntity> getAnchorsByDataspaceIdAndModuleNames(@Param("dataspaceId") int dataspaceId,
- @Param("moduleNames") Collection<String> moduleNames, @Param("sizeOfModuleNames") int sizeOfModuleNames);
+ @Param("moduleNames") String[] moduleNames,
+ @Param("sizeOfModuleNames") int sizeOfModuleNames);
+
+ default Collection<AnchorEntity> getAnchorsByDataspaceIdAndModuleNames(final int dataspaceId,
+ final Collection<String> moduleNames,
+ final int sizeOfModuleNames) {
+ final String[] moduleNamesArray = moduleNames.toArray(new String[0]);
+ return getAnchorsByDataspaceIdAndModuleNames(dataspaceId, moduleNamesArray, sizeOfModuleNames);
+ }
+
+ @Modifying
+ @Query(value = "DELETE FROM anchor WHERE dataspace_id = :dataspaceId AND name = ANY (:anchorNames)",
+ nativeQuery = true)
+ void deleteAllByDataspaceIdAndNameIn(@Param("dataspaceId") int dataspaceId,
+ @Param("anchorNames") String[] anchorNames);
+
+ default void deleteAllByDataspaceAndNameIn(final DataspaceEntity dataspaceEntity,
+ final Collection<String> anchorNames) {
+ deleteAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames.toArray(new String[0]));
+ }
- void deleteAllByDataspaceAndNameIn(DataspaceEntity dataspaceEntity,
- Collection<String> anchorNames);
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java
deleted file mode 100644
index bad68f7e5..000000000
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.spi.repository;
-
-import java.util.Collection;
-
-/**
- * This interface is used in delete fragment entity by id with child using native sql queries.
- */
-public interface FragmentNativeRepository {
-
- /**
- * Delete fragment entities for each supplied xpath.
- * This method will delete list elements or other data nodes, but not whole lists.
- * Non-existing xpaths will not result in an exception.
- * @param anchorId the id of the anchor
- * @param xpaths xpaths of data nodes to remove
- */
- void deleteByAnchorIdAndXpaths(int anchorId, Collection<String> xpaths);
-
- /**
- * Delete fragment entities that are list elements of each supplied list xpath.
- * For example, if xpath '/parent/list' is provided, then list all elements in '/parent/list' will be deleted,
- * e.g. /parent/list[@key='A'], /parent/list[@key='B'].
- * This method will only delete whole lists by xpath; xpaths to list elements or other data nodes will be ignored.
- * Non-existing xpaths will not result in an exception.
- * @param anchorId the id of the anchor
- * @param listXpaths xpaths of whole lists to remove
- */
- void deleteListsByAnchorIdAndXpaths(int anchorId, Collection<String> listXpaths);
-}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java
deleted file mode 100644
index 04b7080de..000000000
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.spi.repository;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.stream.Collectors;
-import javax.persistence.EntityManager;
-import javax.persistence.PersistenceContext;
-import javax.persistence.Query;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor
-public class FragmentNativeRepositoryImpl implements FragmentNativeRepository {
-
- @PersistenceContext
- private final EntityManager entityManager;
-
- @Override
- public void deleteByAnchorIdAndXpaths(final int anchorId, final Collection<String> xpaths) {
- final String queryString =
- "DELETE FROM fragment f WHERE f.anchor_id = ? AND (f.xpath IN (:parameterPlaceholders))";
- executeUpdateWithAnchorIdAndCollection(queryString, anchorId, xpaths);
- }
-
- @Override
- public void deleteListsByAnchorIdAndXpaths(final int anchorId, final Collection<String> listXpaths) {
- final Collection<String> listXpathPatterns =
- listXpaths.stream().map(listXpath -> listXpath + "[%").collect(Collectors.toSet());
- final String queryString =
- "DELETE FROM fragment f WHERE f.anchor_id = ? AND (f.xpath LIKE ANY (array[:parameterPlaceholders]))";
- executeUpdateWithAnchorIdAndCollection(queryString, anchorId, listXpathPatterns);
- }
-
- // Accept security hotspot as placeholders in SQL query are created internally, not from user input.
- @SuppressWarnings("squid:S2077")
- private void executeUpdateWithAnchorIdAndCollection(final String sqlTemplate, final int anchorId,
- final Collection<String> collection) {
- if (!collection.isEmpty()) {
- final String parameterPlaceholders = String.join(",", Collections.nCopies(collection.size(), "?"));
- final String queryStringWithParameterPlaceholders =
- sqlTemplate.replaceFirst(":parameterPlaceholders\\b", parameterPlaceholders);
-
- final Query query = entityManager.createNativeQuery(queryStringWithParameterPlaceholders);
- query.setParameter(1, anchorId);
- int parameterIndex = 2;
- for (final String parameterValue : collection) {
- query.setParameter(parameterIndex++, parameterValue);
- }
- query.executeUpdate();
- }
- }
-
-}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java
index d486a39c7..8114f1055 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java
@@ -37,8 +37,7 @@ import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
-public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, FragmentRepositoryCpsPathQuery,
- FragmentNativeRepository {
+public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, FragmentRepositoryCpsPathQuery {
Optional<FragmentEntity> findByAnchorAndXpath(AnchorEntity anchorEntity, String xpath);
@@ -52,13 +51,39 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
@Query("SELECT f FROM FragmentEntity f WHERE anchor = :anchor")
List<FragmentExtract> findAllExtractsByAnchor(@Param("anchor") AnchorEntity anchorEntity);
- List<FragmentEntity> findAllByAnchorAndXpathIn(AnchorEntity anchorEntity, Collection<String> xpath);
+ @Query(value = "SELECT * FROM fragment WHERE xpath = ANY (:xpaths)", nativeQuery = true)
+ List<FragmentEntity> findAllByXpathIn(@Param("xpaths") String[] xpath);
- List<FragmentEntity> findAllByXpathIn(Collection<String> xpath);
+ default List<FragmentEntity> findAllByXpathIn(final Collection<String> xpaths) {
+ return findAllByXpathIn(xpaths.toArray(new String[0]));
+ }
+
+ @Modifying
+ @Query(value = "DELETE FROM fragment WHERE anchor_id = ANY (:anchorIds)", nativeQuery = true)
+ void deleteByAnchorIdIn(@Param("anchorIds") int[] anchorIds);
+
+ default void deleteByAnchorIn(final Collection<AnchorEntity> anchorEntities) {
+ deleteByAnchorIdIn(anchorEntities.stream().map(AnchorEntity::getId).mapToInt(id -> id).toArray());
+ }
@Modifying
- @Query("DELETE FROM FragmentEntity WHERE anchor IN (:anchors)")
- void deleteByAnchorIn(@Param("anchors") Collection<AnchorEntity> anchorEntities);
+ @Query(value = "DELETE FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)", nativeQuery = true)
+ void deleteByAnchorIdAndXpaths(@Param("anchorId") int anchorId, @Param("xpaths") String[] xpaths);
+
+ default void deleteByAnchorIdAndXpaths(final int anchorId, final Collection<String> xpaths) {
+ deleteByAnchorIdAndXpaths(anchorId, xpaths.toArray(new String[0]));
+ }
+
+ @Modifying
+ @Query(value = "DELETE FROM fragment f WHERE anchor_id = :anchorId AND xpath LIKE ANY (:xpathPatterns)",
+ nativeQuery = true)
+ void deleteByAnchorIdAndXpathLikeAny(@Param("anchorId") int anchorId,
+ @Param("xpathPatterns") String[] xpathPatterns);
+
+ default void deleteListsByAnchorIdAndXpaths(int anchorId, Collection<String> xpaths) {
+ final String[] listXpathPatterns = xpaths.stream().map(xpath -> xpath + "[%").toArray(String[]::new);
+ deleteByAnchorIdAndXpathLikeAny(anchorId, listXpathPatterns);
+ }
@Query("SELECT f FROM FragmentEntity f WHERE anchor = :anchor"
+ " AND (xpath = :parentXpath OR xpath LIKE CONCAT(:parentXpath,'/%'))")
@@ -66,13 +91,6 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
@Param("parentXpath") String parentXpath);
@Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"
- + " CAST(attributes AS TEXT) AS attributes"
- + " FROM FRAGMENT WHERE "
- + "( xpath = :parentXpath OR xpath LIKE CONCAT(:parentXpath,'/%') )",
- nativeQuery = true)
- List<FragmentExtract> findByParentXpath(@Param("parentXpath") String parentXpath);
-
- @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"
+ " CAST(attributes AS TEXT) AS attributes"
+ " FROM FRAGMENT WHERE anchor_id = :anchorId"
+ " AND xpath ~ :xpathRegex",
@@ -80,9 +98,15 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
List<FragmentExtract> quickFindWithDescendants(@Param("anchorId") int anchorId,
@Param("xpathRegex") String xpathRegex);
- @Query("SELECT xpath FROM FragmentEntity WHERE anchor = :anchor AND xpath IN :xpaths")
- List<String> findAllXpathByAnchorAndXpathIn(@Param("anchor") AnchorEntity anchorEntity,
- @Param("xpaths") Collection<String> xpaths);
+ @Query(value = "SELECT xpath FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)",
+ nativeQuery = true)
+ List<String> findAllXpathByAnchorIdAndXpathIn(@Param("anchorId") int anchorId,
+ @Param("xpaths") String[] xpaths);
+
+ default List<String> findAllXpathByAnchorAndXpathIn(final AnchorEntity anchorEntity,
+ final Collection<String> xpaths) {
+ return findAllXpathByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths.toArray(new String[0]));
+ }
boolean existsByAnchorAndXpathStartsWith(AnchorEntity anchorEntity, String xpath);
@@ -93,7 +117,7 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
= "WITH RECURSIVE parent_search AS ("
+ " SELECT id, 0 AS depth "
+ " FROM fragment "
- + " WHERE anchor_id = :anchorId AND xpath IN :xpaths "
+ + " WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths) "
+ " UNION "
+ " SELECT c.id, depth + 1 "
+ " FROM fragment c INNER JOIN parent_search p ON c.parent_id = p.id"
@@ -104,9 +128,14 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
nativeQuery = true
)
List<FragmentExtract> findExtractsWithDescendants(@Param("anchorId") int anchorId,
- @Param("xpaths") Collection<String> xpaths,
+ @Param("xpaths") String[] xpaths,
@Param("maxDepth") int maxDepth);
+ default List<FragmentExtract> findExtractsWithDescendants(final int anchorId, final Collection<String> xpaths,
+ final int maxDepth) {
+ return findExtractsWithDescendants(anchorId, xpaths.toArray(new String[0]), maxDepth);
+ }
+
@Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"
+ " CAST(attributes AS TEXT) AS attributes"
+ " FROM FRAGMENT WHERE xpath ~ :xpathRegex",
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java
index 3263f3447..3c5f973cb 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java
@@ -24,7 +24,6 @@ package org.onap.cps.spi.repository;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
-import java.util.stream.Collectors;
import org.onap.cps.spi.entities.DataspaceEntity;
import org.onap.cps.spi.entities.SchemaSetEntity;
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
@@ -44,7 +43,7 @@ public interface SchemaSetRepository extends JpaRepository<SchemaSetEntity, Inte
* @param dataspaceEntity dataspace entity
* @return list of schema set entity
*/
- Collection<SchemaSetEntity> findByDataspace(DataspaceEntity dataspaceEntity);
+ List<SchemaSetEntity> findByDataspace(DataspaceEntity dataspaceEntity);
Integer countByDataspace(DataspaceEntity dataspaceEntity);
@@ -61,24 +60,20 @@ public interface SchemaSetRepository extends JpaRepository<SchemaSetEntity, Inte
.orElseThrow(() -> new SchemaSetNotFoundException(dataspaceEntity.getName(), schemaSetName));
}
- /**
- * Gets all schema sets for a given dataspace.
- *
- * @param dataspaceEntity dataspace entity
- * @return list of schema set entity
- * @throws SchemaSetNotFoundException if SchemaSet not found
- */
- default List<SchemaSetEntity> getByDataspace(final DataspaceEntity dataspaceEntity) {
- return findByDataspace(dataspaceEntity).stream().collect(Collectors.toList());
- }
+ @Modifying
+ @Query(value = "DELETE FROM schema_set WHERE dataspace_id = :dataspaceId AND name = ANY (:schemaSetNames)",
+ nativeQuery = true)
+ void deleteByDataspaceIdAndNameIn(@Param("dataspaceId") final int dataspaceId,
+ @Param("schemaSetNames") final String[] schemaSetNames);
/**
* Delete multiple schema sets in a given dataspace.
* @param dataspaceEntity dataspace entity
* @param schemaSetNames schema set names
*/
- @Modifying
- @Query("DELETE FROM SchemaSetEntity s WHERE s.dataspace = :dataspaceEntity AND s.name IN (:schemaSetNames)")
- void deleteByDataspaceAndNameIn(@Param("dataspaceEntity") DataspaceEntity dataspaceEntity,
- @Param("schemaSetNames") Collection<String> schemaSetNames);
+ default void deleteByDataspaceAndNameIn(final DataspaceEntity dataspaceEntity,
+ final Collection<String> schemaSetNames) {
+ deleteByDataspaceIdAndNameIn(dataspaceEntity.getId(), schemaSetNames.toArray(new String[0]));
+ }
+
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
index fff0a6a03..7584ff65c 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
@@ -35,7 +35,11 @@ import org.springframework.stereotype.Repository;
public interface YangResourceRepository extends JpaRepository<YangResourceEntity, Long>,
YangResourceNativeRepository, SchemaSetYangResourceRepository {
- List<YangResourceEntity> findAllByChecksumIn(Set<String> checksum);
+ List<YangResourceEntity> findAllByChecksumIn(String[] checksums);
+
+ default List<YangResourceEntity> findAllByChecksumIn(final Collection<String> checksums) {
+ return findAllByChecksumIn(checksums.toArray(new String[0]));
+ }
@Query(value = "SELECT DISTINCT\n"
+ "yang_resource.module_name AS module_name,\n"
@@ -86,9 +90,14 @@ public interface YangResourceRepository extends JpaRepository<YangResourceEntity
+ "schema_set.id\n"
+ "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n"
+ "WHERE\n"
- + "dataspace.name = :dataspaceName and yang_resource.module_Name IN (:moduleNames)", nativeQuery = true)
+ + "dataspace.name = :dataspaceName and yang_resource.module_Name = ANY (:moduleNames)", nativeQuery = true)
Set<YangResourceModuleReference> findAllModuleReferencesByDataspaceAndModuleNames(
- @Param("dataspaceName") String dataspaceName, @Param("moduleNames") Collection<String> moduleNames);
+ @Param("dataspaceName") String dataspaceName, @Param("moduleNames") String[] moduleNames);
+
+ default Set<YangResourceModuleReference> findAllModuleReferencesByDataspaceAndModuleNames(
+ final String dataspaceName, final Collection<String> moduleNames) {
+ return findAllModuleReferencesByDataspaceAndModuleNames(dataspaceName, moduleNames.toArray(new String[0]));
+ }
@Modifying
@Query(value = "DELETE FROM yang_resource yr WHERE NOT EXISTS "
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
index e60afa78d..93d766201 100755
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
@@ -23,7 +23,6 @@
package org.onap.cps.spi.impl
-import com.fasterxml.jackson.databind.ObjectMapper
import com.google.common.collect.ImmutableSet
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.entities.FragmentEntity
@@ -35,7 +34,6 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.DataNodeBuilder
-import org.onap.cps.utils.JsonObjectMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
@@ -49,7 +47,6 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
@Autowired
CpsDataPersistenceService objectUnderTest
- static JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
static DataNodeBuilder dataNodeBuilder = new DataNodeBuilder()
static final String SET_DATA = '/data/fragment.sql'
@@ -353,11 +350,11 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Update data node and descendants by removing descendants.'() {
- given: 'data node object with leaves updated, no children'
- def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [])
+ def 'Update data nodes and descendants by removing descendants.'() {
+ given: 'data nodes with leaves updated, no children'
+ def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [])]
when: 'update data nodes and descendants is performed'
- objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+ objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes)
then: 'leaves have been updated for selected data node'
def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID)
def updatedLeaves = getLeavesMap(updatedFragment)
@@ -370,13 +367,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Update data node and descendants with new descendants'() {
- given: 'data node object with leaves updated, having child with old content'
- def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
+ def 'Update data nodes and descendants with new descendants'() {
+ given: 'data nodes with leaves updated, having child with old content'
+ def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
buildDataNode('/parent-200/child-201/grand-child', ['leaf-value': 'original'], [])
- ])
+ ])]
when: 'update is performed including descendants'
- objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+ objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes)
then: 'leaves have been updated for selected data node'
def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID)
def updatedLeaves = getLeavesMap(updatedFragment)
@@ -390,13 +387,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Update data node and descendants with same descendants but changed leaf value.'() {
- given: 'data node object with leaves updated, having child with old content'
- def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
+ def 'Update data nodes and descendants with same descendants but changed leaf value.'() {
+ given: 'data nodes with leaves updated, having child with old content'
+ def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
buildDataNode('/parent-200/child-201/grand-child', ['leaf-value': 'new'], [])
- ])
+ ])]
when: 'update is performed including descendants'
- objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+ objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes)
then: 'leaves have been updated for selected data node'
def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID)
def updatedLeaves = getLeavesMap(updatedFragment)
@@ -410,13 +407,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Update data node and descendants with different descendants xpath'() {
- given: 'data node object with leaves updated, having child with old content'
- def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
+ def 'Update data nodes and descendants with different descendants xpath'() {
+ given: 'data nodes with leaves updated, having child with old content'
+ def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
buildDataNode('/parent-200/child-201/grand-child-new', ['leaf-value': 'new'], [])
- ])
+ ])]
when: 'update is performed including descendants'
- objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+ objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes)
then: 'leaves have been updated for selected data node'
def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID)
def updatedLeaves = getLeavesMap(updatedFragment)
@@ -432,19 +429,17 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Update data node and descendants error scenario: #scenario.'() {
- given: 'data node object'
- def submittedDataNode = buildDataNode(xpath, ['leaf-name': 'leaf-value'], [])
+ def 'Update data nodes and descendants error scenario: #scenario.'() {
+ given: 'data nodes collection'
+ def submittedDataNodes = [buildDataNode(xpath, ['leaf-name': 'leaf-value'], [])]
when: 'attempt to update data node for #scenario'
- objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, submittedDataNode)
+ objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, submittedDataNodes)
then: 'a #expectedException is thrown'
thrown(expectedException)
where: 'the following data is used'
scenario | dataspaceName | anchorName | xpath || expectedException
'non-existing dataspace' | 'NO DATASPACE' | 'not relevant' | '/not relevant' || DataspaceNotFoundException
'non-existing anchor' | DATASPACE_NAME | 'NO ANCHOR' | '/not relevant' || AnchorNotFoundException
- 'non-existing xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | '/NON-EXISTING-XPATH' || DataNodeNotFoundException
- 'invalid xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | 'INVALID XPATH' || CpsPathException
}
@Sql([CLEAR_DATA, SET_DATA])
@@ -699,7 +694,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
return dataNodeBuilder.withXpath(xpath).withLeaves(leaves).withChildDataNodes(childDataNodes).build()
}
- static Map<String, Object> getLeavesMap(FragmentEntity fragmentEntity) {
+ Map<String, Object> getLeavesMap(FragmentEntity fragmentEntity) {
return jsonObjectMapper.convertJsonString(fragmentEntity.attributes, Map<String, Object>.class)
}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
index f02aa754f..204b93442 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
@@ -77,24 +77,6 @@ class CpsDataPersistenceServiceSpec extends Specification {
1 * objectUnderTest.storeDataNodes('dataspace1', 'anchor1', [dataNode])
}
- def 'Handling of StaleStateException (caused by concurrent updates) during update data node and descendants.'() {
- given: 'the fragment repository returns a fragment entity'
- mockFragmentRepository.getByAnchorAndXpath(*_) >> {
- def fragmentEntity = new FragmentEntity()
- fragmentEntity.setChildFragments([new FragmentEntity()] as Set<FragmentEntity>)
- return fragmentEntity
- }
- and: 'a data node is concurrently updated by another transaction'
- mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
- when: 'attempt to update data node with submitted data nodes'
- objectUnderTest.updateDataNodeAndDescendants('some-dataspace', 'some-anchor', new DataNodeBuilder().withXpath('/some/xpath').build())
- then: 'concurrency exception is thrown'
- def concurrencyException = thrown(ConcurrencyException)
- assert concurrencyException.getDetails().contains('some-dataspace')
- assert concurrencyException.getDetails().contains('some-anchor')
- assert concurrencyException.getDetails().contains('/some/xpath')
- }
-
def 'Handling of StaleStateException (caused by concurrent updates) during update data nodes and descendants.'() {
given: 'the system can update one datanode and has two more datanodes that throw an exception while updating'
def dataNodes = createDataNodesAndMockRepositoryMethodSupportingThem([
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy
index 214fd69ff..65d63dfe3 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2022 Bell Canada.
+ * Modifications Copyright (C) 2021-2023 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
@@ -19,18 +20,14 @@
*/
package org.onap.cps.spi.impl
-import com.fasterxml.jackson.databind.ObjectMapper
+
import org.hibernate.exception.ConstraintViolationException
-import org.onap.cps.spi.CpsAdminPersistenceService
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.entities.DataspaceEntity
import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
import org.onap.cps.spi.model.ModuleReference
-import org.onap.cps.spi.repository.AnchorRepository
import org.onap.cps.spi.repository.DataspaceRepository
-import org.onap.cps.spi.repository.SchemaSetRepository
import org.onap.cps.spi.repository.YangResourceRepository
-import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.dao.DataIntegrityViolationException
@@ -43,24 +40,12 @@ class CpsModulePersistenceServiceConcurrencySpec extends CpsPersistenceSpecBase
@Autowired
CpsModulePersistenceService objectUnderTest
- @Autowired
- AnchorRepository anchorRepository
-
- @Autowired
- SchemaSetRepository schemaSetRepository
-
- @Autowired
- CpsAdminPersistenceService cpsAdminPersistenceService
-
@SpringBean
YangResourceRepository yangResourceRepositoryMock = Mock()
@SpringBean
DataspaceRepository dataspaceRepositoryMock = Mock()
- @SpringBean
- JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
-
static final String DATASPACE_NAME = 'DATASPACE-001'
static final String SCHEMA_SET_NAME_NEW = 'SCHEMA-SET-NEW'
static final String NEW_RESOURCE_NAME = 'some new resource'
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy
index 864b3e3b6..53f42f5a9 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy
@@ -30,13 +30,10 @@ import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
import org.onap.cps.spi.model.ModuleDefinition
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.spi.model.SchemaSet
-import org.onap.cps.spi.repository.AnchorRepository
import org.onap.cps.spi.repository.SchemaSetRepository
import org.onap.cps.spi.repository.SchemaSetYangResourceRepositoryImpl
-import org.onap.cps.spi.repository.YangResourceRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
-import spock.lang.Ignore
class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
@@ -44,17 +41,11 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase
CpsModulePersistenceService objectUnderTest
@Autowired
- AnchorRepository anchorRepository
-
- @Autowired
SchemaSetRepository schemaSetRepository
@Autowired
CpsAdminPersistenceService cpsAdminPersistenceService
- @Autowired
- YangResourceRepository yangResourceRepository
-
final static String SET_DATA = '/data/schemaset.sql'
def static EXISTING_SCHEMA_SET_NAME = SCHEMA_SET_NAME1
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy
index 9ef973268..811c3290b 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy
@@ -84,7 +84,7 @@ class CpsModulePersistenceServiceSpec extends Specification {
def 'Store schema set error scenario: #scenario.'() {
given: 'no yang resource are currently saved'
- yangResourceRepositoryMock.findAllByChecksumIn(_) >> Collections.emptyList()
+ yangResourceRepositoryMock.findAllByChecksumIn(_ as Collection<String>) >> Collections.emptyList()
and: 'persisting yang resource raises db constraint exception (in case of concurrent requests for example)'
yangResourceRepositoryMock.saveAll(_) >> { throw dbException }
when: 'attempt to store schema set '
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy
index 9b722cdda..222a828b9 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,6 @@ import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.entities.SchemaSetEntity
import org.onap.cps.spi.impl.CpsPersistenceSpecBase
import org.onap.cps.spi.model.ModuleReference
-import org.onap.cps.spi.repository.DataspaceRepository
import org.onap.cps.spi.repository.ModuleReferenceRepository
import org.onap.cps.spi.repository.SchemaSetRepository
import org.springframework.beans.factory.annotation.Autowired
@@ -53,9 +52,6 @@ class CpsModuleReferenceRepositoryPerfTest extends CpsPersistenceSpecBase {
CpsModulePersistenceService objectUnderTest
@Autowired
- DataspaceRepository dataspaceRepository
-
- @Autowired
SchemaSetRepository schemaSetRepository
@Autowired
diff --git a/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java b/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java
index 54d6ff395..5ee6b3804 100644
--- a/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java
+++ b/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START========================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,11 +20,7 @@
package org.onap.cps.cache;
-import com.hazelcast.config.Config;
import com.hazelcast.config.MapConfig;
-import com.hazelcast.config.NamedConfig;
-import com.hazelcast.core.Hazelcast;
-import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -33,7 +29,7 @@ import org.springframework.context.annotation.Configuration;
* Core infrastructure of the hazelcast distributed cache for anchor data config use cases.
*/
@Configuration
-public class AnchorDataCacheConfig {
+public class AnchorDataCacheConfig extends HazelcastCacheConfig {
private static final MapConfig anchorDataCacheMapConfig = createMapConfig("anchorDataCacheMapConfig");
@@ -44,27 +40,7 @@ public class AnchorDataCacheConfig {
*/
@Bean
public IMap<String, AnchorDataCacheEntry> anchorDataCache() {
- return createHazelcastInstance("hazelCastInstanceCpsCore", anchorDataCacheMapConfig)
- .getMap("anchorDataCache");
+ return createHazelcastInstance("hazelCastInstanceCpsCore", anchorDataCacheMapConfig, "cps-service-caches")
+ .getMap("anchorDataCache");
}
-
- private HazelcastInstance createHazelcastInstance(final String hazelcastInstanceName,
- final NamedConfig namedConfig) {
- return Hazelcast.newHazelcastInstance(initializeConfig(hazelcastInstanceName, namedConfig));
- }
-
- private Config initializeConfig(final String instanceName, final NamedConfig namedConfig) {
- final Config config = new Config(instanceName);
- config.addMapConfig((MapConfig) namedConfig);
- config.setClusterName("cps-service-caches");
- return config;
- }
-
- private static MapConfig createMapConfig(final String configName) {
- final MapConfig mapConfig = new MapConfig(configName);
- mapConfig.setBackupCount(3);
- mapConfig.setAsyncBackupCount(3);
- return mapConfig;
- }
-
}
diff --git a/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java b/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java
new file mode 100644
index 000000000..4aebceae0
--- /dev/null
+++ b/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java
@@ -0,0 +1,85 @@
+/*
+ * ============LICENSE_START========================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.cache;
+
+import com.hazelcast.config.Config;
+import com.hazelcast.config.MapConfig;
+import com.hazelcast.config.NamedConfig;
+import com.hazelcast.config.QueueConfig;
+import com.hazelcast.core.Hazelcast;
+import com.hazelcast.core.HazelcastInstance;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+
+/**
+ * Core infrastructure of the hazelcast distributed cache.
+ */
+@Slf4j
+public class HazelcastCacheConfig {
+
+ @Value("${hazelcast.mode.kubernetes.enabled}")
+ protected boolean cacheKubernetesEnabled;
+
+ @Value("${hazelcast.mode.kubernetes.service-name}")
+ protected String cacheKubernetesServiceName;
+
+ protected HazelcastInstance createHazelcastInstance(final String hazelcastInstanceName,
+ final NamedConfig namedConfig, final String clusterName) {
+ return Hazelcast.newHazelcastInstance(initializeConfig(hazelcastInstanceName, namedConfig, clusterName));
+ }
+
+ private Config initializeConfig(final String instanceName, final NamedConfig namedConfig,
+ final String clusterName) {
+ final Config config = new Config(instanceName);
+ if (namedConfig instanceof MapConfig) {
+ config.addMapConfig((MapConfig) namedConfig);
+ }
+ if (namedConfig instanceof QueueConfig) {
+ config.addQueueConfig((QueueConfig) namedConfig);
+ }
+ config.setClusterName(clusterName);
+ updateDiscoveryMode(config);
+ return config;
+ }
+
+ protected static MapConfig createMapConfig(final String configName) {
+ final MapConfig mapConfig = new MapConfig(configName);
+ mapConfig.setBackupCount(3);
+ mapConfig.setAsyncBackupCount(3);
+ return mapConfig;
+ }
+
+ protected static QueueConfig createQueueConfig(final String configName) {
+ final QueueConfig commonQueueConfig = new QueueConfig(configName);
+ commonQueueConfig.setBackupCount(3);
+ commonQueueConfig.setAsyncBackupCount(3);
+ return commonQueueConfig;
+ }
+
+ protected void updateDiscoveryMode(final Config config) {
+ if (cacheKubernetesEnabled) {
+ log.info("Enabling kubernetes mode with service-name : {}", cacheKubernetesServiceName);
+ config.getNetworkConfig().getJoin().getKubernetesConfig().setEnabled(true)
+ .setProperty("service-name", cacheKubernetesServiceName);
+ }
+ }
+
+}
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 540401913..949fbc2c2 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
@@ -138,15 +138,6 @@ public interface CpsDataPersistenceService {
void updateDataLeaves(String dataspaceName, String anchorName, String xpath, Map<String, Serializable> leaves);
/**
- * Replaces an existing data node's content including descendants.
- *
- * @param dataspaceName dataspace name
- * @param anchorName anchor name
- * @param dataNode data node
- */
- void updateDataNodeAndDescendants(String dataspaceName, String anchorName, DataNode dataNode);
-
- /**
* Replaces multiple existing data nodes' content including descendants in a batch operation.
*
* @param dataspaceName dataspace name
diff --git a/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy
index 839444b68..76b534553 100644
--- a/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
*/
package org.onap.cps.cache
+
+import com.hazelcast.config.Config
import com.hazelcast.core.Hazelcast
import com.hazelcast.map.IMap
import org.springframework.beans.factory.annotation.Autowired
@@ -49,4 +51,28 @@ class AnchorDataCacheConfigSpec extends Specification {
assert anchorDataCacheConfig.backupCount == 3
assert anchorDataCacheConfig.asyncBackupCount == 3
}
+
+ def 'Verify deployment network configs for Distributed Caches'() {
+ given: 'the Anchor Data Cache config'
+ def anchorDataCacheNetworkConfig = Hazelcast.getHazelcastInstanceByName('hazelCastInstanceCpsCore').config.networkConfig
+ expect: 'system created instance with correct config'
+ assert anchorDataCacheNetworkConfig.join.autoDetectionConfig.enabled
+ assert !anchorDataCacheNetworkConfig.join.kubernetesConfig.enabled
+ }
+
+ def 'Verify network config'() {
+ given: 'Synchronization config object and test configuration'
+ def objectUnderTest = new AnchorDataCacheConfig()
+ def testConfig = new Config()
+ when: 'kubernetes properties are enabled'
+ objectUnderTest.cacheKubernetesEnabled = true
+ objectUnderTest.cacheKubernetesServiceName = 'test-service-name'
+ and: 'method called to update the discovery mode'
+ objectUnderTest.updateDiscoveryMode(testConfig)
+ then: 'applied properties are reflected'
+ assert testConfig.networkConfig.join.kubernetesConfig.enabled
+ assert testConfig.networkConfig.join.kubernetesConfig.properties.get('service-name') == 'test-service-name'
+
+ }
+
}
diff --git a/cps-service/src/test/resources/application.yml b/cps-service/src/test/resources/application.yml
index 04295eb74..21f374533 100644
--- a/cps-service/src/test/resources/application.yml
+++ b/cps-service/src/test/resources/application.yml
@@ -48,3 +48,10 @@ spring:
logging:
level:
org.apache.kafka: ERROR
+
+# Custom Hazelcast Config.
+hazelcast:
+ mode:
+ kubernetes:
+ enabled: false
+ service-name: "cps-and-ncmp-service"
diff --git a/dmi-plugin-stub/mappings/batchCmHandles.json b/dmi-plugin-stub/mappings/batchCmHandles.json
index 2018516b4..5f290cd47 100644
--- a/dmi-plugin-stub/mappings/batchCmHandles.json
+++ b/dmi-plugin-stub/mappings/batchCmHandles.json
@@ -1,7 +1,7 @@
{
"request": {
"method": "POST",
- "urlPattern": "/dmi/v1/ch/batch/data/ds/.*"
+ "urlPattern": "/dmi/v1/batch/data/ds/.*"
},
"response": {
"status": 200,
diff --git a/docs/cps-path.rst b/docs/cps-path.rst
index a24bf1e9f..08892e09e 100644
--- a/docs/cps-path.rst
+++ b/docs/cps-path.rst
@@ -223,7 +223,7 @@ descendant-path
leaf-conditions
---------------
-**Syntax**: ``<xpath> '[' @<leaf-name1> '=' <leaf-value1> ( ' and ' @<leaf-name> '=' <leaf-value> )* ']'``
+**Syntax**: ``<xpath> '[' @<leaf-name1> '=' <leaf-value1> ( ' <and|or> ' @<leaf-name> '=' <leaf-value> )* ']'``
- ``xpath``: Absolute or descendant or xpath to the (list) node which elements will be queried.
- ``leaf-name``: The name of the leaf which value needs to be compared.
- ``leaf-value``: The required value of the leaf.
@@ -233,10 +233,13 @@ leaf-conditions
- ``//categories[@name="Kids"]``
- ``//categories[@name='Kids']``
- ``//categories[@code='1']/books/book[@title='Dune' and @price=5]``
+ - ``//categories[@code='1']/books/book[@title='xyz' or @price=15]``
- ``//categories[@code=1]``
**Limitations**
- Only the last list or container can be queried leaf values. Any ancestor list will have to be referenced by its key name-value pair(s).
- - Multiple attributes can only be combined using ``and``. ``or`` and bracketing is not supported.
+ - When mixing ``and/or`` operators, ``and`` has precedence over ``or`` . So ``and`` operators get evaluated first.
+ - Bracketing is not supported.
+ - Leaf names are not validated so ``or`` operations with invalid leaf names will silently be ignored.
- Only leaves can be used, leaf-list are not supported.
- Only string and integer values are supported, boolean and float values are not supported.
- The key should be supplied with correct data type for it to be queried from DB. In the last example above the attribute code is of type
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy
index 44287b221..a04302fa6 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy
@@ -62,19 +62,21 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
and: 'the cps-path of queryDataNodes has the expectedLeaves'
assert result.leaves.sort() == expectedLeaves.sort()
where: 'the following data is used'
- scenario | cpspath || expectedResultSize | expectedLeaves
- 'the "OR" condition' | '//books[@lang="English" or @price=15]' || 6 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
- [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]],
- [lang: "English", price: 14, title: "The Light Fantastic", authors: ["Terry Pratchett"], editions: [1986]],
- [lang: "English", price: 13, title: "Good Omens", authors: ["Terry Pratchett", "Neil Gaiman"], editions: [2006]],
- [lang: "English", price: 12, title: "The Colour of Magic", authors: ["Terry Pratchett"], editions: [1983]],
- [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]]]
- 'the "OR" condition with non-json data' | '//books[@title="xyz" or @price=15]' || 2 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
- [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]]
- 'combination of multiple AND' | '//books[@lang="English" and @price=15 and @edition=1983]' || 0 | []
- 'combination of multiple OR' | '//books[ @title="Matilda" or @price=15 or @edition=1983]' || 3 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
- [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]],
- [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]]
+ scenario | cpspath || expectedResultSize | expectedLeaves
+ 'the "OR" condition' | '//books[@lang="English" or @price=15]' || 6 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
+ [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]],
+ [lang: "English", price: 14, title: "The Light Fantastic", authors: ["Terry Pratchett"], editions: [1986]],
+ [lang: "English", price: 13, title: "Good Omens", authors: ["Terry Pratchett", "Neil Gaiman"], editions: [2006]],
+ [lang: "English", price: 12, title: "The Colour of Magic", authors: ["Terry Pratchett"], editions: [1983]],
+ [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]]]
+ 'the "OR" condition with non-json data' | '//books[@title="xyz" or @price=15]' || 2 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
+ [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]]
+ 'combination of multiple AND' | '//books[@lang="English" and @price=15 and @edition=1983]' || 0 | []
+ 'combination of multiple OR' | '//books[ @title="Matilda" or @price=15 or @edition=1983]' || 3 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
+ [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]],
+ [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]]
+ 'combination of AND/OR' | '//books[@edition=1983 and @price=15 or @title="Good Omens"]' || 1 | [[lang: "English", price: 13, title: "Good Omens", authors: ["Terry Pratchett", "Neil Gaiman"], editions: [2006]]]
+ 'combination of OR/AND' | '//books[@title="Annihilation" or @price=39 and @lang="arabic"]' || 1 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]]]
}
def 'Cps Path query for leaf value(s) with #scenario.'() {
@@ -169,17 +171,19 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
def bookTitles = result.collect { it.getLeaves().get('title') }
assert bookTitles.sort() == expectedBookTitles.sort()
where: 'the following data is used'
- scenario | cpsPath || expectedBookTitles
- 'one leaf' | '//books[@price=14]' || ['The Light Fantastic']
- 'one text' | '//books/authors[text()="Terry Pratchett"]' || ['Good Omens', 'The Colour of Magic', 'The Light Fantastic']
- 'more than one leaf' | '//books[@price=12 and @lang="English"]' || ['The Colour of Magic']
- 'more than one leaf has "OR" condition' | '//books[@lang="English" or @price=15]' || ['Annihilation', 'Good Omens', 'Matilda', 'The Colour of Magic', 'The Gruffalo', 'The Light Fantastic']
- 'more than one leaf has "OR" condition with non-json data' | '//books[@title="xyz" or @price=13]' || ['Good Omens']
- 'more than one leaf has multiple AND' | '//books[@lang="English" and @price=13 and @edition=1983]' || []
- 'more than one leaf has multiple OR' | '//books[ @title="Matilda" or @price=15 or @edition=2006]' || ['Annihilation', 'Matilda', 'The Gruffalo']
- 'leaves reversed in order' | '//books[@lang="English" and @price=12]' || ['The Colour of Magic']
- 'leaf and text' | '//books[@price=14]/authors[text()="Terry Pratchett"]' || ['The Light Fantastic']
- 'leaf and contains' | '//books[contains(@price,"13")]' || ['Good Omens']
+ scenario | cpsPath || expectedBookTitles
+ 'one leaf' | '//books[@price=14]' || ['The Light Fantastic']
+ 'one text' | '//books/authors[text()="Terry Pratchett"]' || ['Good Omens', 'The Colour of Magic', 'The Light Fantastic']
+ 'more than one leaf' | '//books[@price=12 and @lang="English"]' || ['The Colour of Magic']
+ 'more than one leaf has "OR" condition' | '//books[@lang="English" or @price=15]' || ['Annihilation', 'Good Omens', 'Matilda', 'The Colour of Magic', 'The Gruffalo', 'The Light Fantastic']
+ 'more than one leaf has "OR" condition with non-json data' | '//books[@title="xyz" or @price=13]' || ['Good Omens']
+ 'more than one leaf has multiple AND' | '//books[@lang="English" and @price=13 and @edition=1983]' || []
+ 'more than one leaf has multiple OR' | '//books[ @title="Matilda" or @price=15 or @edition=2006]' || ['Annihilation', 'Matilda', 'The Gruffalo']
+ 'leaves reversed in order' | '//books[@lang="English" and @price=12]' || ['The Colour of Magic']
+ 'more than one leaf has combination of AND/OR' | '//books[@edition=1983 and @price=13 or @title="Good Omens"]' || ['Good Omens']
+ 'more than one leaf has OR/AND' | '//books[@title="The Light Fantastic" or @price=11 and @edition=1983]' || ['The Light Fantastic']
+ 'leaf and text' | '//books[@price=14]/authors[text()="Terry Pratchett"]' || ['The Light Fantastic']
+ 'leaf and contains' | '//books[contains(@price,"13")]' || ['Good Omens']
}
def 'Cps Path query using descendant anywhere with #scenario condition(s) for a list element.'() {
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimits.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimits.groovy
index 7875caec3..0034af453 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimits.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimits.groovy
@@ -22,7 +22,6 @@ package org.onap.cps.integration.performance.cps
import org.onap.cps.api.CpsAdminService
import org.onap.cps.integration.performance.base.CpsPerfTestBase
-import org.springframework.dao.DataAccessResourceFailureException
class CpsAdminServiceLimits extends CpsPerfTestBase {
@@ -32,20 +31,20 @@ class CpsAdminServiceLimits extends CpsPerfTestBase {
def 'Get anchors from multiple schema set names limit exceeded: 32,766 (~ 2^15) schema set names.'() {
given: 'more than 32,766 schema set names'
- def schemaSetNames = (0..32_766).collect { "size-of-this-name-does-not-matter-for-limit-" + it }
+ def schemaSetNames = (0..40_000).collect { "size-of-this-name-does-not-matter-for-limit-" + it }
when: 'single get is executed to get all the anchors'
objectUnderTest.getAnchors(CPS_PERFORMANCE_TEST_DATASPACE, schemaSetNames)
- then: 'a database exception is thrown'
- thrown(DataAccessResourceFailureException.class)
+ then: 'a database exception is not thrown'
+ noExceptionThrown()
}
def 'Querying anchor names limit exceeded: 32,766 (~ 2^15) modules.'() {
given: 'more than 32,766 module names'
- def moduleNames = (0..32_766).collect { "size-of-this-name-does-not-matter-for-limit-" + it }
+ def moduleNames = (0..40_000).collect { "size-of-this-name-does-not-matter-for-limit-" + it }
when: 'single query is executed to get all the anchors'
objectUnderTest.queryAnchorNames(CPS_PERFORMANCE_TEST_DATASPACE, moduleNames)
- then: 'a database exception is thrown'
- thrown(DataAccessResourceFailureException.class)
+ then: 'a database exception is not thrown'
+ noExceptionThrown()
}
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimits.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimits.groovy
index 2df910194..1579470ea 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimits.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimits.groovy
@@ -23,8 +23,7 @@ package org.onap.cps.integration.performance.cps
import java.time.OffsetDateTime
import org.onap.cps.api.CpsDataService
import org.onap.cps.integration.performance.base.CpsPerfTestBase
-import org.springframework.dao.DataAccessResourceFailureException
-import org.springframework.transaction.TransactionSystemException
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
@@ -36,29 +35,29 @@ class CpsDataServiceLimits extends CpsPerfTestBase {
def 'Multiple get limit exceeded: 32,764 (~ 2^15) xpaths.'() {
given: 'more than 32,764 xpaths'
- def xpaths = (0..32_764).collect { "/size/of/this/path/does/not/matter/for/limit[@id='" + it + "']" }
+ def xpaths = (0..40_000).collect { "/size/of/this/path/does/not/matter/for/limit[@id='" + it + "']" }
when: 'single operation is executed to get all datanodes with given xpaths'
objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'bookstore1', xpaths, INCLUDE_ALL_DESCENDANTS)
- then: 'a database exception is thrown'
- thrown(DataAccessResourceFailureException.class)
+ then: 'a database exception is not thrown'
+ noExceptionThrown()
}
def 'Delete multiple datanodes limit exceeded: 32,767 (~ 2^15) xpaths.'() {
given: 'more than 32,767 xpaths'
- def xpaths = (0..32_767).collect { "/size/of/this/path/does/not/matter/for/limit[@id='" + it + "']" }
+ def xpaths = (0..40_000).collect { "/size/of/this/path/does/not/matter/for/limit[@id='" + it + "']" }
when: 'single operation is executed to delete all datanodes with given xpaths'
objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'bookstore1', xpaths, OffsetDateTime.now())
- then: 'a database exception is thrown'
- thrown(TransactionSystemException.class)
+ then: 'a database exception is not thrown (but a CPS DataNodeNotFoundException is thrown)'
+ thrown(DataNodeNotFoundException.class)
}
def 'Delete datanodes from multiple anchors limit exceeded: 32,766 (~ 2^15) anchors.'() {
given: 'more than 32,766 anchor names'
- def anchorNames = (0..32_766).collect { "size-of-this-name-does-not-matter-for-limit-" + it }
+ def anchorNames = (0..40_000).collect { "size-of-this-name-does-not-matter-for-limit-" + it }
when: 'single operation is executed to delete all datanodes in given anchors'
objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchorNames, OffsetDateTime.now())
- then: 'a database exception is thrown'
- thrown(DataAccessResourceFailureException.class)
+ then: 'a database exception is not thrown'
+ noExceptionThrown()
}
}