aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--checkstyle/pom.xml2
-rw-r--r--checkstyle/src/main/resources/cps-java-style.xml2
-rw-r--r--cps-application/pom.xml8
-rw-r--r--cps-application/src/main/resources/application.yml30
-rw-r--r--cps-application/src/test/java/org/onap/cps/architecture/LayeredArchitectureTest.java72
-rw-r--r--cps-application/src/test/java/org/onap/cps/architecture/NcmpArchitectureTest.java98
-rw-r--r--cps-bom/pom.xml2
-rw-r--r--cps-dependencies/pom.xml12
-rw-r--r--cps-events/pom.xml2
-rw-r--r--cps-ncmp-events/pom.xml2
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/cmnotificationsubscription/ncmp-out-event-schema-1.0.0.json28
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-1.0.0.json22
-rw-r--r--cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml2
-rw-r--r--cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml2
-rw-r--r--cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java3
-rw-r--r--cps-ncmp-rest-stub/pom.xml2
-rw-r--r--cps-ncmp-rest/docs/openapi/components.yaml8
-rwxr-xr-xcps-ncmp-rest/docs/openapi/ncmp.yml22
-rwxr-xr-xcps-ncmp-rest/docs/openapi/openapi-inventory.yml6
-rwxr-xr-xcps-ncmp-rest/docs/openapi/openapi.yml4
-rw-r--r--cps-ncmp-rest/pom.xml21
-rwxr-xr-xcps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java73
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java7
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy48
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy33
-rw-r--r--cps-ncmp-service/pom.xml6
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java18
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobResultService.java45
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java15
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobStatusService.java43
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java7
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/NcmpException.java13
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PolicyExecutorException.java43
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java59
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/DmiHttpClientConfig.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/HttpClientConfiguration.java)17
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java72
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java47
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/ServiceConfig.java36
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfig.java (renamed from cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java)36
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java6
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandler.java22
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumer.java11
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java103
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumer.java4
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionKey.java (renamed from dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiOperationCmHandle.java)28
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionTuple.java34
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandler.java3
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImpl.java138
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumer.java2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapper.java8
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducer.java33
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java15
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java21
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java6
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java39
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/PolicyExecutor.java74
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java225
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImpl.java55
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java13
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImpl.java66
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java38
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiRestClient.java39
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfiguration.java68
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/AlternateIdChecker.java108
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java12
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java33
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationService.java29
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java16
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java16
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java35
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java8
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DataSyncWatchdog.java9
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java6
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java110
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java31
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java32
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java69
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java27
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventType.java4
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsService.java55
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumer.java2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDog.java8
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java7
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java77
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfiguration.java (renamed from dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/config/NcmpRequestLoggingConfig.java)31
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java15
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiServiceUrlTemplateBuilder.java)39
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/UrlTemplateParameters.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/UrlTemplateParameters.java)2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/WebClientConfiguration.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientConfiguration.java)57
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CmAvcEventPublisher.java1
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/DmiHttpClientConfigSpec.groovy (renamed from cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/HttpClientConfigurationSpec.groovy)56
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy81
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy113
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy53
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfigSpec.groovy (renamed from cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy)17
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfigSpec.groovy6
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandlerSpec.groovy29
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumerSpec.groovy5
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapperSpec.groovy52
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumerSpec.groovy2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImplSpec.groovy70
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumerSpec.groovy2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapperSpec.groovy6
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy19
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy15
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy14
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandlerSpec.groovy17
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacadeSpec.groovy7
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy73
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy47
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy234
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImplSpec.groovy55
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImplSpec.groovy7
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImplSpec.groovy53
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy22
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy25
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiWebClientConfigurationSpec.groovy77
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfigurationSpec.groovy70
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/AlternateIdCheckerSpec.groovy76
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy62
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServiceSpec.groovy8
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy31
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy105
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperationsSpec.groovy2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy85
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy33
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasksSpec.groovy85
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdogSpec.groovy10
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfigSpec.groovy46
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsServiceSpec.groovy55
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumerSpec.groovy2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDogSpec.groovy4
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfigSpec.groovy24
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy70
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfigurationSpec.groovy53
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy14
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilderSpec.groovy (renamed from cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiServiceUrlTemplateBuilderSpec.groovy)15
-rw-r--r--cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/WebClientBuilderTestConfig.java40
-rw-r--r--cps-ncmp-service/src/test/resources/application.yml50
-rw-r--r--cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json7
-rw-r--r--cps-parent/pom.xml30
-rw-r--r--cps-path-parser/pom.xml2
-rw-r--r--cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g427
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java82
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java18
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java8
-rw-r--r--cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy42
-rw-r--r--cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy59
-rw-r--r--cps-rest/docs/openapi/components.yml29
-rw-r--r--cps-rest/docs/openapi/cpsData.yml28
-rw-r--r--cps-rest/docs/openapi/cpsDataV2.yml8
-rw-r--r--cps-rest/docs/openapi/openapi.yml15
-rw-r--r--cps-rest/pom.xml21
-rwxr-xr-xcps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java64
-rw-r--r--cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java21
-rwxr-xr-xcps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy97
-rw-r--r--cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy23
-rw-r--r--cps-ri/pom.xml7
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/ri/CpsAdminPersistenceServiceImpl.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java)14
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java)47
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java)27
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/models/AnchorEntity.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/entities/AnchorEntity.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/models/DataspaceEntity.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/entities/DataspaceEntity.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/models/FragmentEntity.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/models/SchemaSetEntity.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/entities/SchemaSetEntity.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceEntity.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceEntity.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceModuleReference.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceModuleReference.java)2
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java)41
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/ri/repository/DataspaceRepository.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java)4
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepository.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepository.java)4
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepositoryImpl.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java)6
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java)180
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java)274
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java)8
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java)15
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceQuery.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java)9
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepository.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepository.java)4
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java181
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java)14
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepository.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepository.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepositoryImpl.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepositoryImpl.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/TempTableCreator.java)32
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepository.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepository.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepositoryImpl.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepositoryImpl.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java)12
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/utils/CpsSessionFactory.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/config/CpsSessionFactory.java)10
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/utils/CpsValidatorImpl.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/impl/utils/CpsValidatorImpl.java)4
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/utils/EscapeUtils.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/utils/EscapeUtils.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/utils/SessionManager.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java)11
-rw-r--r--cps-ri/src/main/java/org/onap/cps/ri/utils/TimeLimiterProvider.java (renamed from cps-ri/src/main/java/org/onap/cps/spi/utils/TimeLimiterProvider.java)2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java87
-rw-r--r--cps-ri/src/main/resources/changelog/db/changes/data/dmi/generated-csv/README.md23
-rw-r--r--cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2021-12-13.yang63
-rw-r--r--cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2022-02-10.yang81
-rw-r--r--cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2022-05-10.yang123
-rw-r--r--cps-ri/src/main/resources/yangResourceCsvGenerator.py66
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy (renamed from cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy)20
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceConcurrencySpec.groovy (renamed from cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy)14
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceImplSpec.groovy (renamed from cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy)15
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/ri/utils/CpsValidatorImplSpec.groovy (renamed from cps-ri/src/test/groovy/org/onap/cps/spi/impl/utils/CpsValidatorSpec.groovy)5
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/ri/utils/EscapeUtilsSpec.groovy (renamed from cps-ri/src/test/groovy/org/onap/cps/spi/utils/EscapeUtilsSpec.groovy)2
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/ri/utils/SessionManagerSpec.groovy (renamed from cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerSpec.groovy)12
-rw-r--r--cps-service/pom.xml25
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/CpsDataService.java41
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java33
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java2
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java45
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java2
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java5
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java13
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java2
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java2
-rw-r--r--cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java45
-rw-r--r--cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheEntry.java44
-rw-r--r--cps-service/src/main/java/org/onap/cps/events/EventsPublisher.java4
-rw-r--r--cps-service/src/main/java/org/onap/cps/impl/utils/CpsValidator.java (renamed from cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java)2
-rwxr-xr-xcps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java16
-rw-r--r--cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java4
-rw-r--r--cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java8
-rw-r--r--cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java92
-rw-r--r--cps-service/src/main/java/org/onap/cps/utils/YangParser.java34
-rw-r--r--cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java63
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy2
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy94
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy2
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy14
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy30
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy2
-rwxr-xr-xcps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy6
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy3
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy80
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheEntrySpec.groovy40
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy13
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/spi/model/DeltaReportBuilderSpec.groovy10
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy2
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy67
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy33
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy48
-rw-r--r--cps-service/src/test/resources/application.yml10
-rw-r--r--cps-service/src/test/resources/bookstore-categories-data.json49
-rw-r--r--cps-service/src/test/resources/bookstore-categories-data.xml14
-rw-r--r--cps-service/src/test/resources/e2e/basic/cps-cavsta-onap-internal2021-01-28.yang2
-rw-r--r--cps-service/src/test/resources/e2e/basic/cps-ran-inventory@2021-01-28.yang2
-rw-r--r--cps-service/src/test/resources/e2e/basic/cps-ran-schema-model@2021-05-19.yang4
-rwxr-xr-xcps-service/src/test/resources/e2e/basic/ran-network2020-08-06.yang2
-rwxr-xr-xcsit/install-deps.sh36
-rw-r--r--csit/plans/cps/pnfsim/docker-compose.yml5
-rw-r--r--csit/plans/cps/pnfsim/tls/ca.pem23
-rw-r--r--csit/plans/cps/pnfsim/tls/server_cert.pem21
-rw-r--r--csit/plans/cps/pnfsim/tls/server_key.pem28
-rw-r--r--csit/plans/cps/sdnc/certs/keys0.zipbin5057 -> 3769 bytes
-rwxr-xr-xcsit/plans/cps/setup.sh9
-rwxr-xr-xcsit/plans/cps/teardown.sh10
-rw-r--r--csit/plans/cps/testplanNcmp.txt2
-rwxr-xr-xcsit/prepare-csit.sh2
-rw-r--r--csit/pylibs.txt2
-rwxr-xr-xcsit/run-project-csit.sh3
-rw-r--r--csit/tests/cps-data-operations/cps-data-operations.robot13
-rw-r--r--csit/tests/cps-data-sync/cps-data-sync.robot16
-rw-r--r--csit/tests/cps-model-sync/cps-model-sync.robot30
-rw-r--r--csit/tests/cps-trust-level/cps-trust-level.robot4
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml113
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/DmiDemoApplication.java32
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml61
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java323
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DataOperationRequest.java39
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiDataOperationRequest.java33
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ResourceFileReaderUtil.java51
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/application.yml64
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/data/operational/ietf-network-topology-sample-rfc8345.json76
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/ietfYang-ModuleResourcesResponse.json52
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/ietfYang-ModuleResponse.json44
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagA-ModuleResourcesResponse.json12
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagA-ModuleResponse.json12
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagB-ModuleResourcesResponse.json12
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagB-ModuleResponse.json12
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagC-ModuleResourcesResponse.json12
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagC-ModuleResponse.json12
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagD-ModuleResourcesResponse.json12
-rw-r--r--dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagD-ModuleResponse.json12
-rw-r--r--dmi-plugin-demo-and-csit-stub/pom.xml42
-rw-r--r--docker-compose/config/grafana/jvm-micrometer-dashboard.json3613
-rw-r--r--docker-compose/config/grafana/provisioning/dashboards/dashboard.yml9
-rw-r--r--docker-compose/config/grafana/provisioning/datasources/datasource.yml8
-rw-r--r--docker-compose/config/nginx/nginx.conf (renamed from docker-compose/nginx.conf)1
-rw-r--r--docker-compose/config/nginx/proxy_params (renamed from docker-compose/proxy_params)0
-rw-r--r--docker-compose/config/postgres-init.sql (renamed from docker-compose/postgres-init.sql)0
-rw-r--r--docker-compose/config/prometheus.yml14
-rw-r--r--docker-compose/docker-compose.yml61
-rw-r--r--docker-compose/prometheus.yml10
-rw-r--r--docs/_static/cps-delta-mechanism.pngbin69416 -> 46182 bytes
-rw-r--r--docs/_static/logo_onap_2024.pngbin0 -> 11627 bytes
-rw-r--r--docs/admin-guide.rst4
-rw-r--r--docs/api/swagger/cps/openapi.yaml102
-rw-r--r--docs/api/swagger/ncmp/openapi-inventory.yaml2
-rw-r--r--docs/api/swagger/ncmp/openapi.yaml92
-rw-r--r--docs/api/swagger/policy-executor/openapi.yaml347
-rw-r--r--docs/architecture.rst2
-rw-r--r--docs/cm-notification-subscriptions.rst48
-rwxr-xr-xdocs/conf.py2
-rw-r--r--docs/cps-delta-endpoints.rst59
-rw-r--r--docs/cps-delta-feature.rst17
-rw-r--r--docs/cps-events.rst1
-rw-r--r--docs/cps-scheduled-processes.rst14
-rw-r--r--docs/deployment.rst38
-rw-r--r--docs/overview.rst4
-rw-r--r--docs/release-notes.rst587
-rw-r--r--docs/schemas/ncmp-in-event-schema-1.0.0.json73
-rw-r--r--docs/schemas/ncmp-out-event-schema-1.0.0.json63
-rw-r--r--docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json29
-rw-r--r--docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json25
-rw-r--r--docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json29
-rw-r--r--docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json29
-rw-r--r--integration-test/pom.xml7
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy51
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy45
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy78
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy58
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy13
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy3
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy2
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy54
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy12
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy84
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpdateSpec.groovy89
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpgradeSpec.groovy40
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobResultServiceSpec.groovy44
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy23
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy65
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy74
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/RestApiSpec.groovy27
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/WriteSubJobSpec.groovy75
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy6
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy4
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy5
-rw-r--r--integration-test/src/test/java/org/onap/cps/integration/DmiStubTestContainer.java61
-rw-r--r--integration-test/src/test/resources/application.yml18
-rw-r--r--jacoco-report/pom.xml2
-rw-r--r--k6-tests/README.md3
-rwxr-xr-xk6-tests/install-deps.sh47
-rw-r--r--k6-tests/ncmp/common/cmhandle-crud.js71
-rw-r--r--k6-tests/ncmp/common/passthrough-crud.js74
-rw-r--r--k6-tests/ncmp/common/search-base.js63
-rw-r--r--k6-tests/ncmp/common/utils.js100
-rw-r--r--k6-tests/ncmp/ncmp-kpi.js343
-rwxr-xr-xk6-tests/ncmp/run-all-tests.sh4
-rw-r--r--k6-tests/once-off-test/kafka/produce-avc-event.js99
-rw-r--r--k6-tests/resources/sampleAvcInputEvent.json38
-rwxr-xr-xk6-tests/run-k6-tests.sh4
-rwxr-xr-xk6-tests/setup.sh4
-rwxr-xr-xk6-tests/teardown.sh16
-rw-r--r--policy-executor-stub/pom.xml21
-rw-r--r--policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java58
-rw-r--r--policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/Sleeper.java (renamed from k6-tests/ncmp/common/passthrough-read.js)23
-rw-r--r--policy-executor-stub/src/main/resources/application.yml5
-rw-r--r--policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy102
-rw-r--r--pom.xml3
-rw-r--r--postman-collections/CPS Environment.postman_environment.json24
-rw-r--r--postman-collections/DMI Stub.postman_collection.json161
-rw-r--r--releases/3.5.1-container.yaml8
-rw-r--r--releases/3.5.1.yaml4
-rw-r--r--releases/3.5.2-container.yaml8
-rw-r--r--releases/3.5.2.yaml4
-rw-r--r--releases/3.5.3-container.yaml8
-rw-r--r--releases/3.5.3.yaml4
-rw-r--r--releases/3.5.4-container.yaml8
-rw-r--r--releases/3.5.4.yaml4
-rw-r--r--spotbugs/pom.xml2
-rw-r--r--spotbugs/src/main/resources/spotbugs-exclude.xml1
-rw-r--r--version.properties2
373 files changed, 11480 insertions, 5283 deletions
diff --git a/README.md b/README.md
index 61a3af4b14..f463368e85 100644
--- a/README.md
+++ b/README.md
@@ -21,9 +21,9 @@
# Configuration Persistence Service
## General Information
-* [CPS Project Wiki](https://wiki.onap.org/display/DW/Configuration+Persistence+Service+Project)
+* [CPS Project Wiki](https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16398157/Configuration+Persistence+Service+Project)
## For Developers
-* [Developer Wiki](https://wiki.onap.org/display/DW/Configuration+Persistence+Service+Developer%27s+Landing+Page)
+* [Developer Wiki](https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16442177/Configuration+Persistence+Service+Developer+s+Landing+Page)
* [Building and running CPS locally](docker-compose/README.md)
diff --git a/checkstyle/pom.xml b/checkstyle/pom.xml
index 0f328e6ceb..893af40162 100644
--- a/checkstyle/pom.xml
+++ b/checkstyle/pom.xml
@@ -26,7 +26,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>checkstyle</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<profiles>
<profile>
diff --git a/checkstyle/src/main/resources/cps-java-style.xml b/checkstyle/src/main/resources/cps-java-style.xml
index 67b2863695..d10484c31e 100644
--- a/checkstyle/src/main/resources/cps-java-style.xml
+++ b/checkstyle/src/main/resources/cps-java-style.xml
@@ -32,4 +32,4 @@
</module>
<module name="UnusedImports"/>
</module>
-</module> \ No newline at end of file
+</module>
diff --git a/cps-application/pom.xml b/cps-application/pom.xml
index 8ab314ea5b..c37447306c 100644
--- a/cps-application/pom.xml
+++ b/cps-application/pom.xml
@@ -28,7 +28,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
@@ -84,11 +84,6 @@
<!-- T E S T D E P E N D E N C I E S -->
<dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<scope>test</scope>
@@ -111,6 +106,7 @@
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index 6f0807113d..3789e92b08 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -160,6 +160,7 @@ cps:
endpoint: ${ONAP_OTEL_EXPORTER_ENDPOINT:http://onap-otel-collector:4317}
protocol: ${ONAP_OTEL_EXPORTER_PROTOCOL:grpc}
enabled: ${ONAP_TRACING_ENABLED:false}
+ excluded-observation-names: ${ONAP_EXCLUDED_OBSERVATION_NAMES:tasks.scheduled.execution}
# Actuator
management:
@@ -187,22 +188,36 @@ logging:
onap:
cps: INFO
ncmp:
- dmi:
+ policy-executor:
+ enabled: ${POLICY_SERVICE_ENABLED:false}
+ defaultDecision: ${POLICY_SERVICE_DEFAULT_DECISION:"allow"}
+ server:
+ address: ${POLICY_SERVICE_URL:http://policy-executor-stub}
+ port: ${POLICY_SERVICE_PORT:8093}
httpclient:
- data-services:
+ all-services:
+ maximumInMemorySizeInMegabytes: 16
+ maximumConnectionsTotal: 100
+ pendingAcquireMaxCount: 50
connectionTimeoutInSeconds: 30
readTimeoutInSeconds: 30
writeTimeoutInSeconds: 30
+ dmi:
+ httpclient:
+ data-services:
+ maximumInMemorySizeInMegabytes: 16
maximumConnectionsTotal: 100
pendingAcquireMaxCount: 50
- maximumInMemorySizeInMegabytes: 16
- model-services:
connectionTimeoutInSeconds: 30
readTimeoutInSeconds: 30
writeTimeoutInSeconds: 30
+ model-services:
+ maximumInMemorySizeInMegabytes: 16
maximumConnectionsTotal: 100
pendingAcquireMaxCount: 50
- maximumInMemorySizeInMegabytes: 16
+ connectionTimeoutInSeconds: 30
+ readTimeoutInSeconds: 30
+ writeTimeoutInSeconds: 30
auth:
username: ${DMI_USERNAME:cpsuser}
password: ${DMI_PASSWORD:cpsr0cks!}
@@ -213,8 +228,6 @@ ncmp:
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:
@@ -234,6 +247,7 @@ ncmp:
# Custom Hazelcast Config.
hazelcast:
cluster-name: ${CPS_NCMP_CACHES_CLUSTER_NAME:"cps-and-ncmp-common-cache-cluster"}
+ instance-config-name: ${CPS_NCMP_INSTANCE_CONFIG_NAME:"cps-and-ncmp-hazelcast-instance-config"}
mode:
kubernetes:
enabled: ${HAZELCAST_MODE_KUBERNETES_ENABLED:false}
@@ -243,4 +257,4 @@ otel:
exporter:
otlp:
traces:
- protocol: ${ONAP_OTEL_EXPORTER_OTLP_TRACES_PROTOCOL:grpc} \ No newline at end of file
+ protocol: ${ONAP_OTEL_EXPORTER_OTLP_TRACES_PROTOCOL:grpc}
diff --git a/cps-application/src/test/java/org/onap/cps/architecture/LayeredArchitectureTest.java b/cps-application/src/test/java/org/onap/cps/architecture/LayeredArchitectureTest.java
deleted file mode 100644
index 91452a7fc5..0000000000
--- a/cps-application/src/test/java/org/onap/cps/architecture/LayeredArchitectureTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2024 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.architecture;
-
-
-import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
-import static com.tngtech.archunit.library.freeze.FreezingArchRule.freeze;
-
-import com.tngtech.archunit.core.importer.ImportOption;
-import com.tngtech.archunit.junit.AnalyzeClasses;
-import com.tngtech.archunit.junit.ArchIgnore;
-import com.tngtech.archunit.junit.ArchTest;
-import com.tngtech.archunit.lang.ArchRule;
-
-/**
- * Test class responsible for layered architecture.
- */
-@AnalyzeClasses(packages = "org.onap.cps", importOptions = {ImportOption.DoNotIncludeTests.class})
-public class LayeredArchitectureTest {
-
- private static final String REST_CONTROLLER_PACKAGE = "org.onap.cps.rest..";
- private static final String NCMP_REST_PACKAGE = "org.onap.cps.ncmp.rest..";
- private static final String API_SERVICE_PACKAGE = "org.onap.cps.api..";
- private static final String SPI_SERVICE_PACKAGE = "org.onap.cps.spi..";
- private static final String NCMP_SERVICE_PACKAGE = "org.onap.cps.ncmp.api..";
- private static final String SPI_REPOSITORY_PACKAGE = "org.onap.cps.spi.repository..";
- private static final String YANG_SCHEMA_PACKAGE = "org.onap.cps.yang..";
- private static final String NOTIFICATION_PACKAGE = "org.onap.cps.notification..";
- private static final String CPS_UTILS_PACKAGE = "org.onap.cps.utils..";
- private static final String NCMP_INIT_PACKAGE = "org.onap.cps.ncmp.init..";
- private static final String CPS_CACHE_PACKAGE = "org.onap.cps.cache..";
- private static final String CPS_EVENTS_PACKAGE = "org.onap.cps.events..";
-
- //TODO We need to revisit these rules, the first one doesn't even make any sense: CPS-2293
-
- @ArchTest
- static final ArchRule restControllerShouldOnlyDependOnRestController =
- classes().that().resideInAPackage(REST_CONTROLLER_PACKAGE).should().onlyHaveDependentClassesThat()
- .resideInAPackage(REST_CONTROLLER_PACKAGE);
-
- @ArchTest
- @ArchIgnore
- static final ArchRule apiOrSpiServiceShouldOnlyBeDependedOnByControllerAndServicesAndCommonUtilityPackages =
- freeze(classes().that().resideInAPackage(API_SERVICE_PACKAGE)
- .or().resideInAPackage(SPI_SERVICE_PACKAGE).should().onlyHaveDependentClassesThat()
- .resideInAnyPackage(REST_CONTROLLER_PACKAGE, API_SERVICE_PACKAGE, SPI_SERVICE_PACKAGE, NCMP_REST_PACKAGE,
- NCMP_SERVICE_PACKAGE, YANG_SCHEMA_PACKAGE, NOTIFICATION_PACKAGE, CPS_UTILS_PACKAGE, NCMP_INIT_PACKAGE,
- CPS_CACHE_PACKAGE, CPS_EVENTS_PACKAGE));
-
-
- @ArchTest
- static final ArchRule repositoryShouldOnlyBeDependedOnByServicesAndRepository =
- classes().that().resideInAPackage(SPI_REPOSITORY_PACKAGE).should().onlyHaveDependentClassesThat()
- .resideInAnyPackage(API_SERVICE_PACKAGE, SPI_SERVICE_PACKAGE, SPI_REPOSITORY_PACKAGE);
-}
diff --git a/cps-application/src/test/java/org/onap/cps/architecture/NcmpArchitectureTest.java b/cps-application/src/test/java/org/onap/cps/architecture/NcmpArchitectureTest.java
new file mode 100644
index 0000000000..4c0b82fec8
--- /dev/null
+++ b/cps-application/src/test/java/org/onap/cps/architecture/NcmpArchitectureTest.java
@@ -0,0 +1,98 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.architecture;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+
+import com.tngtech.archunit.core.importer.ImportOption;
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * These test verify the correct use of dependencies in all CPS modules.
+ */
+@AnalyzeClasses(packages = "org.onap.cps", importOptions = {ImportOption.DoNotIncludeTests.class})
+public class NcmpArchitectureTest {
+
+ private static final String[] ACCEPTED_3PP_PACKAGES = { "com.fasterxml..",
+ "com.google..",
+ "com.hazelcast..",
+ "edu..",
+ "io.cloudevents..",
+ "io.micrometer..",
+ "io.netty..",
+ "io.swagger..",
+ "jakarta..",
+ "java..",
+ "lombok..",
+ "org.apache..",
+ "org.mapstruct..",
+ "org.slf4j..",
+ "org.springframework..",
+ "reactor.."
+ };
+
+ @ArchTest
+ static final ArchRule nothingDependsOnCpsNcmpRest =
+ classes().that().resideInAPackage("org.onap.cps.ncmp.rest..").should().onlyHaveDependentClassesThat()
+ .resideInAPackage("org.onap.cps.ncmp.rest..");
+
+ @ArchTest
+ static final ArchRule ncmpRestControllerShouldOnlyDependOnNcmpService =
+ classes().that().resideInAPackage("org.onap.cps.ncmp.rest..")
+ .should()
+ .onlyDependOnClassesThat()
+ .resideInAnyPackage(commonAndListedPackages("org.onap.cps.ncmp.rest..", "org.onap.cps.ncmp.api..",
+ // Below packages are breaking the agreed dependencies
+ // and need to be removed from this rule.
+ // This will be handled in a separate user story
+ "org.onap.cps.spi..", "org.onap.cps.utils..", "org.onap.cps.ncmp.impl.."));
+
+ @ArchTest
+ static final ArchRule ncmpServiceApiShouldOnlyDependOnThirdPartyPackages =
+ classes().that().resideInAPackage("org.onap.cps.ncmp.api..").should().onlyDependOnClassesThat()
+ .resideInAnyPackage(commonAndListedPackages(
+ // Below packages are breaking the agreed dependencies
+ // and need to be removed from this rule.
+ // This will be handled in a separate user story
+ "org.onap.cps.spi..", "org.onap.cps.ncmp.api..", "org.onap.cps.ncmp.impl..",
+ "org.onap.cps.ncmp.config", "org.onap.cps.utils.."));
+
+ @ArchTest
+ static final ArchRule ncmpServiceImplShouldOnlyDependOnCpsServiceAndNcmpEvents =
+ classes().that().resideInAPackage("org.onap.cps.ncmp.impl..").should().onlyDependOnClassesThat()
+ .resideInAnyPackage(commonAndListedPackages(
+ "org.onap.cps.ncmp.api..", "org.onap.cps.ncmp.impl..",
+ "org.onap.cps.ncmp.event..", "org.onap.cps.ncmp.events..", "org.onap.cps.ncmp.utils..",
+ "org.onap.cps.ncmp.config..", "org.onap.cps.api..", "org.onap.cps.ncmp.exceptions..",
+ // Below packages are breaking the agreed dependencies
+ // and need to be removed from this rule.
+ // This will be handled in a separate user story
+ "org.onap.cps.spi..", "org.onap.cps.events..", "org.onap.cps.cpspath..",
+ "org.onap.cps.impl..", "org.onap.cps.utils.."));
+
+ static String[] commonAndListedPackages(final String... packageIdentifiers) {
+ return ArrayUtils.addAll(ACCEPTED_3PP_PACKAGES, packageIdentifiers);
+ }
+
+}
+
diff --git a/cps-bom/pom.xml b/cps-bom/pom.xml
index 93274b810a..6521830f2a 100644
--- a/cps-bom/pom.xml
+++ b/cps-bom/pom.xml
@@ -25,7 +25,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>cps-bom</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<packaging>pom</packaging>
<description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
diff --git a/cps-dependencies/pom.xml b/cps-dependencies/pom.xml
index 16b1697efc..a95e5f42b0 100644
--- a/cps-dependencies/pom.xml
+++ b/cps-dependencies/pom.xml
@@ -27,7 +27,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>cps-dependencies</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.groupId}:${project.artifactId}</name>
@@ -126,12 +126,12 @@
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs</artifactId>
- <version>4.2.3</version>
+ <version>4.8.6</version>
</dependency>
<dependency>
- <groupId>com.google.code.findbugs</groupId>
- <artifactId>annotations</artifactId>
- <version>3.0.1</version>
+ <groupId>com.github.spotbugs</groupId>
+ <artifactId>spotbugs-annotations</artifactId>
+ <version>4.8.6</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
@@ -249,7 +249,7 @@
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
- <version>4.21.0</version>
+ <version>4.29.0</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
diff --git a/cps-events/pom.xml b/cps-events/pom.xml
index 1af13cc918..4c951cb362 100644
--- a/cps-events/pom.xml
+++ b/cps-events/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
diff --git a/cps-ncmp-events/pom.xml b/cps-ncmp-events/pom.xml
index befae04f8d..0ca3a51dc1 100644
--- a/cps-ncmp-events/pom.xml
+++ b/cps-ncmp-events/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
diff --git a/cps-ncmp-events/src/main/resources/schemas/cmnotificationsubscription/ncmp-out-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/cmnotificationsubscription/ncmp-out-event-schema-1.0.0.json
index 0c8d02a613..11dc4e1114 100644
--- a/cps-ncmp-events/src/main/resources/schemas/cmnotificationsubscription/ncmp-out-event-schema-1.0.0.json
+++ b/cps-ncmp-events/src/main/resources/schemas/cmnotificationsubscription/ncmp-out-event-schema-1.0.0.json
@@ -28,32 +28,26 @@
"description": "The unique subscription id"
},
"acceptedTargets": {
- "type": "array",
- "description": "List of accepted targets",
- "items": {
- "type": "string"
- }
+ "type": "object",
+ "existingJavaType": "java.util.Collection<String>",
+ "description": "Unique Collection of accepted targets"
},
"rejectedTargets": {
- "type": "array",
- "description": "List of rejected targets",
- "items": {
- "type": "string"
- }
+ "type": "object",
+ "existingJavaType": "java.util.Collection<String>",
+ "description": "Unique Collection of rejected targets"
},
"pendingTargets": {
- "type": "array",
- "description": "List of pending targets",
- "items": {
- "type": "string"
- }
+ "type": "object",
+ "existingJavaType": "java.util.Collection<String>",
+ "description": "Unique Collection of pending targets"
}
},
"required": [
+ "subscriptionId",
"acceptedTargets",
- "pendingTargets",
"rejectedTargets",
- "subscriptionId"
+ "pendingTargets"
],
"title": "Data"
}
diff --git a/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-1.0.0.json
index a5bed939bf..474520d142 100644
--- a/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-1.0.0.json
+++ b/cps-ncmp-events/src/main/resources/schemas/dmidataavc/avc-event-schema-1.0.0.json
@@ -16,7 +16,8 @@
"type": "string"
},
"value": {
- "$ref": "#/definitions/Value"
+ "type": "object",
+ "existingJavaType": "java.lang.Object"
}
},
"required": [
@@ -25,25 +26,6 @@
"target"
]
},
- "Value": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "attributes": {
- "type": "array",
- "items": {
- "type": "object",
- "existingJavaType": "java.util.Map<String,Object>",
- "additionalProperties": false,
- "properties": {
- "isHoAllowed": {
- "type": "boolean"
- }
- }
- }
- }
- }
- },
"AvcEvent": {
"description": "The payload for AVC event.",
"type": "object",
diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml
index 6e21c0475c..67e1ed29ed 100644
--- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml
+++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-ncmp-rest-stub</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
</parent>
<artifactId>cps-ncmp-rest-stub-app</artifactId>
diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml
index 15c79683f2..1fcea8f567 100644
--- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml
+++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-ncmp-rest-stub</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
</parent>
<artifactId>cps-ncmp-rest-stub-service</artifactId>
diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
index 11adc84a1f..183698c4f5 100644
--- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
+++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
@@ -165,7 +165,8 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
}
@Override
- public ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(final String cmHandle) {
+ public ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(
+ final String cmHandleReference) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
diff --git a/cps-ncmp-rest-stub/pom.xml b/cps-ncmp-rest-stub/pom.xml
index 9fad293173..aecdde1528 100644
--- a/cps-ncmp-rest-stub/pom.xml
+++ b/cps-ncmp-rest-stub/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml
index a9311d0861..112dddf61c 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -505,6 +505,14 @@ components:
schema:
type: string
example: my-cm-handle
+ cmHandleReferenceInPath:
+ name: cm-handle
+ in: path
+ description: The identifier (cmHandle or alternate) for a network function, network element, subnetwork or any other cm object by managed Network CM Proxy
+ required: true
+ schema:
+ type: string
+ example: my-cm-handle-reference
moduleNameInQuery:
name: module-name
in: query
diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml
index d0b1f35eaa..adb2419c8a 100755
--- a/cps-ncmp-rest/docs/openapi/ncmp.yml
+++ b/cps-ncmp-rest/docs/openapi/ncmp.yml
@@ -27,7 +27,7 @@ resourceDataForCmHandle:
operationId: getResourceDataForCmHandle
parameters:
- $ref: 'components.yaml#/components/parameters/datastoreName'
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
- $ref: 'components.yaml#/components/parameters/topicParamInQuery'
@@ -60,7 +60,7 @@ resourceDataForCmHandle:
operationId: createResourceDataRunningForCmHandle
parameters:
- $ref: 'components.yaml#/components/parameters/datastoreName'
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/contentParamInHeader'
- $ref: 'components.yaml#/components/parameters/authorizationParamInHeader'
@@ -99,7 +99,7 @@ resourceDataForCmHandle:
operationId: updateResourceDataRunningForCmHandle
parameters:
- $ref: 'components.yaml#/components/parameters/datastoreName'
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/contentParamInHeader'
- $ref: 'components.yaml#/components/parameters/authorizationParamInHeader'
@@ -138,7 +138,7 @@ resourceDataForCmHandle:
operationId: patchResourceDataRunningForCmHandle
parameters:
- $ref: 'components.yaml#/components/parameters/datastoreName'
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/contentParamInHeader'
- $ref: 'components.yaml#/components/parameters/authorizationParamInHeader'
@@ -171,7 +171,7 @@ resourceDataForCmHandle:
operationId: deleteResourceDataRunningForCmHandle
parameters:
- $ref: 'components.yaml#/components/parameters/datastoreName'
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/contentParamInHeader'
- $ref: 'components.yaml#/components/parameters/authorizationParamInHeader'
@@ -194,7 +194,7 @@ dataOperationForCmHandle:
tags:
- network-cm-proxy
summary: Execute a data operation for group 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. A maximum of 50 cm handles per operation is supported.
+ 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. A maximum of 200 cm handles per operation is supported.
operationId: executeDataOperationForCmHandles
parameters:
- $ref: 'components.yaml#/components/parameters/requiredTopicParamInQuery'
@@ -264,7 +264,7 @@ fetchModuleReferencesByCmHandle:
summary: Fetch all module references (name and revision) for a given cm handle
operationId: getModuleReferencesByCmHandle
parameters:
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
responses:
200:
description: OK
@@ -289,7 +289,7 @@ getModuleDefinitions:
description: Get module definitions (module name, revision, yang resource) with options to filter on module name and revision
operationId: getModuleDefinitions
parameters:
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
- $ref: 'components.yaml#/components/parameters/moduleNameInQuery'
- $ref: 'components.yaml#/components/parameters/revisionInQuery'
responses:
@@ -354,7 +354,7 @@ retrieveCmHandleDetailsById:
summary: Retrieve CM handle details
operationId: retrieveCmHandleDetailsById
parameters:
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
responses:
200:
description: OK
@@ -377,7 +377,7 @@ getCmHandlePropertiesById:
summary: Get CM handle properties
operationId: getCmHandlePublicPropertiesByCmHandleId
parameters:
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
responses:
200:
description: OK
@@ -400,7 +400,7 @@ getCmHandleStateById:
summary: Get CM handle state
operationId: getCmHandleStateByCmHandleId
parameters:
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+ - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath'
responses:
200:
description: OK
diff --git a/cps-ncmp-rest/docs/openapi/openapi-inventory.yml b/cps-ncmp-rest/docs/openapi/openapi-inventory.yml
index b794082875..d374dcc9db 100755
--- a/cps-ncmp-rest/docs/openapi/openapi-inventory.yml
+++ b/cps-ncmp-rest/docs/openapi/openapi-inventory.yml
@@ -1,6 +1,6 @@
# ============LICENSE_START=======================================================
# Copyright (C) 2021 Bell Canada
-# Modifications Copyright (C) 2022-2023 Nordix Foundation
+# Modifications Copyright (C) 2022-2024 Nordix Foundation
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ openapi: 3.0.3
info:
title: NCMP Inventory API
description: NCMP Inventory API
- version: "1.0"
+ version: "3.5.4"
servers:
- url: /ncmpInventory
components:
@@ -40,4 +40,4 @@ paths:
$ref: 'ncmp-inventory.yml#/searchCmHandleIds'
security:
- - basicAuth: [] \ No newline at end of file
+ - basicAuth: []
diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml
index dd6d7c8baa..dd2706a795 100755
--- a/cps-ncmp-rest/docs/openapi/openapi.yml
+++ b/cps-ncmp-rest/docs/openapi/openapi.yml
@@ -22,7 +22,7 @@ openapi: 3.0.3
info:
title: NCMP to CPS Proxy API
description: NCMP to CPS Proxy API
- version: "1.0"
+ version: "3.5.4"
servers:
- url: /ncmp
components:
@@ -64,4 +64,4 @@ paths:
/v1/ch/{cm-handle}/data-sync:
$ref: 'ncmp.yml#/setDataSyncEnabledFlag'
security:
- - basicAuth: [] \ No newline at end of file
+ - basicAuth: []
diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml
index 7faec78ec7..c78fe55a59 100644
--- a/cps-ncmp-rest/pom.xml
+++ b/cps-ncmp-rest/pom.xml
@@ -27,7 +27,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
@@ -231,6 +231,25 @@
</resources>
</configuration>
</execution>
+ <execution>
+ <id>copy-to-docs-folder</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.basedir}/../docs/api/swagger/ncmp</outputDirectory>
+ <overwrite>true</overwrite>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/target/generated-sources/openapi/</directory>
+ <includes>
+ <include>openapi*.yaml</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
</executions>
</plugin>
</plugins>
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 a482cf5a3c..42f709dcf5 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
@@ -86,7 +86,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
* Get resource data from datastore.
*
* @param datastoreName name of the datastore
- * @param cmHandle cm handle identifier
+ * @param cmHandleReference cm handle or alternate id identifier
* @param resourceIdentifier resource identifier
* @param optionsParamInQuery options query parameter
* @param topicParamInQuery topic query parameter
@@ -97,15 +97,16 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
@Override
@Timed(value = "cps.ncmp.controller.get", description = "Time taken to get resource data from datastore")
public ResponseEntity<Object> getResourceDataForCmHandle(final String datastoreName,
- final String cmHandle,
+ final String cmHandleReference,
final String resourceIdentifier,
final String optionsParamInQuery,
final String topicParamInQuery,
final Boolean includeDescendants,
final String authorization) {
- final CmResourceAddress cmResourceAddress = new CmResourceAddress(datastoreName, cmHandle, resourceIdentifier);
+ final CmResourceAddress cmResourceAddress = new CmResourceAddress(datastoreName, cmHandleReference,
+ resourceIdentifier);
final Object result = networkCmProxyFacade.getResourceDataForCmHandle(cmResourceAddress, optionsParamInQuery,
- topicParamInQuery, includeDescendants, authorization);
+ topicParamInQuery, includeDescendants, authorization);
return ResponseEntity.ok(result);
}
@@ -147,7 +148,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
* Patch resource data.
*
* @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running")
- * @param cmHandle cm handle identifier
+ * @param cmHandleReference cm handle or alternate identifier
* @param resourceIdentifier resource identifier
* @param requestBody the request body
* @param contentType content type of body
@@ -157,7 +158,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
@Override
public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String datastoreName,
- final String cmHandle,
+ final String cmHandleReference,
final String resourceIdentifier,
final Object requestBody,
final String contentType,
@@ -167,7 +168,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final Object responseObject = networkCmProxyFacade
.writeResourceDataPassThroughRunningForCmHandle(
- cmHandle, resourceIdentifier, PATCH,
+ cmHandleReference, resourceIdentifier, PATCH,
jsonObjectMapper.asJsonString(requestBody), contentType, authorization);
return ResponseEntity.ok(responseObject);
}
@@ -176,7 +177,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
* Create resource data for given cm-handle.
*
* @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running")
- * @param cmHandle cm handle identifier
+ * @param cmHandleReference cm handle or alternate identifier
* @param resourceIdentifier resource identifier
* @param requestBody the request body
* @param contentType content type of body
@@ -185,14 +186,14 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
*/
@Override
public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String datastoreName,
- final String cmHandle,
+ final String cmHandleReference,
final String resourceIdentifier,
final Object requestBody,
final String contentType,
final String authorization) {
validateDataStore(PASSTHROUGH_RUNNING, datastoreName);
- networkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
+ networkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle(cmHandleReference,
resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType, authorization);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@@ -201,7 +202,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
* Update resource data for given cm-handle.
*
* @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running")
- * @param cmHandle cm handle identifier
+ * @param cmHandleReference cm handle or alternate identifier
* @param resourceIdentifier resource identifier
* @param requestBody the request body
* @param contentType content type of the body
@@ -211,14 +212,14 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
@Override
public ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String datastoreName,
- final String cmHandle,
+ final String cmHandleReference,
final String resourceIdentifier,
final Object requestBody,
final String contentType,
final String authorization) {
validateDataStore(PASSTHROUGH_RUNNING, datastoreName);
- networkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
+ networkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle(cmHandleReference,
resourceIdentifier, UPDATE, jsonObjectMapper.asJsonString(requestBody), contentType, authorization);
return new ResponseEntity<>(HttpStatus.OK);
}
@@ -227,7 +228,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
* Delete resource data for a given cm-handle.
*
* @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running")
- * @param cmHandle cm handle identifier
+ * @param cmHandleReference cm handle or alternate identifier
* @param resourceIdentifier resource identifier
* @param contentType content type of the body
* @param authorization contents of Authorization header, or null if not present
@@ -235,14 +236,14 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
*/
@Override
public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName,
- final String cmHandle,
+ final String cmHandleReference,
final String resourceIdentifier,
final String contentType,
final String authorization) {
validateDataStore(PASSTHROUGH_RUNNING, datastoreName);
- networkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
+ networkCmProxyFacade.writeResourceDataPassThroughRunningForCmHandle(cmHandleReference,
resourceIdentifier, DELETE, NO_BODY, contentType, authorization);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@@ -285,28 +286,28 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
/**
* Search for Cm Handle and Properties by Name.
*
- * @param cmHandleId cm-handle identifier
+ * @param cmHandleReference cm-handle or alternate identifier
* @return cm handle and its properties
*/
@Override
- public ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleId) {
+ public ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleReference) {
final NcmpServiceCmHandle ncmpServiceCmHandle
- = networkCmProxyInventoryFacade.getNcmpServiceCmHandle(cmHandleId);
+ = networkCmProxyInventoryFacade.getNcmpServiceCmHandle(cmHandleReference);
final RestOutputCmHandle restOutputCmHandle = toRestOutputCmHandle(ncmpServiceCmHandle);
return ResponseEntity.ok(restOutputCmHandle);
}
/**
- * Get Cm Handle Properties by Cm Handle Id.
+ * Get Cm Handle Properties by Cm Handle or alternate Identifier.
*
- * @param cmHandleId cm-handle identifier
+ * @param cmHandleReference cm-handle or alternate identifier
* @return cm handle properties
*/
@Override
public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
- final String cmHandleId) {
+ final String cmHandleReference) {
final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties();
- cmHandlePublicProperties.add(networkCmProxyInventoryFacade.getCmHandlePublicProperties(cmHandleId));
+ cmHandlePublicProperties.add(networkCmProxyInventoryFacade.getCmHandlePublicProperties(cmHandleReference));
final RestOutputCmHandlePublicProperties restOutputCmHandlePublicProperties =
new RestOutputCmHandlePublicProperties();
restOutputCmHandlePublicProperties.setPublicCmHandleProperties(cmHandlePublicProperties);
@@ -316,13 +317,13 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
/**
* Get Cm Handle State by Cm Handle Id.
*
- * @param cmHandleId cm-handle identifier
+ * @param cmHandleReference cm-handle or alternate identifier
* @return cm handle state
*/
@Override
public ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(
- final String cmHandleId) {
- final CompositeState cmHandleState = networkCmProxyInventoryFacade.getCmHandleCompositeState(cmHandleId);
+ final String cmHandleReference) {
+ final CompositeState cmHandleState = networkCmProxyInventoryFacade.getCmHandleCompositeState(cmHandleReference);
final RestOutputCmHandleCompositeState restOutputCmHandleCompositeState =
new RestOutputCmHandleCompositeState();
restOutputCmHandleCompositeState.setState(
@@ -333,21 +334,23 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
/**
* Return module definitions.
*
- * @param cmHandleId cm-handle identifier
- * @param moduleName module name
- * @param revision the revision of the module
+ * @param cmHandleReference cm handle or alternate id identifier
+ * @param moduleName module name
+ * @param revision the revision of the module
* @return list of module definitions (module name, revision, yang resource content)
*/
@Override
- public ResponseEntity<List<RestModuleDefinition>> getModuleDefinitions(final String cmHandleId,
+ public ResponseEntity<List<RestModuleDefinition>> getModuleDefinitions(final String cmHandleReference,
final String moduleName,
final String revision) {
final Collection<ModuleDefinition> moduleDefinitions;
if (StringUtils.hasText(moduleName)) {
moduleDefinitions =
- networkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, revision);
+ networkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleAndModule(cmHandleReference,
+ moduleName, revision);
} else {
- moduleDefinitions = networkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleId(cmHandleId);
+ moduleDefinitions =
+ networkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleReference(cmHandleReference);
if (StringUtils.hasText(revision)) {
log.warn("Ignoring revision filter as no module name is provided");
}
@@ -362,12 +365,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
/**
* Return module references for a cm handle.
*
- * @param cmHandle the cm handle
+ * @param cmHandleReference cm handle or alternate id identifier
* @return module references for cm handle. Namespace will be always blank because restConf does not include this.
*/
- public ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandle) {
+ public ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandleReference) {
final List<RestModuleReference> restModuleReferences =
- networkCmProxyInventoryFacade.getYangResourcesModuleReferences(cmHandle).stream()
+ networkCmProxyInventoryFacade.getYangResourcesModuleReferences(cmHandleReference).stream()
.map(ncmpRestInputMapper::toRestModuleReference)
.collect(Collectors.toList());
return new ResponseEntity<>(restModuleReferences, HttpStatus.OK);
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java
index d61d30dc2b..6910003c54 100644
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java
@@ -31,6 +31,7 @@ import org.onap.cps.ncmp.api.exceptions.DmiRequestException;
import org.onap.cps.ncmp.api.exceptions.InvalidTopicException;
import org.onap.cps.ncmp.api.exceptions.NcmpException;
import org.onap.cps.ncmp.api.exceptions.PayloadTooLargeException;
+import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException;
import org.onap.cps.ncmp.api.exceptions.ServerNcmpException;
import org.onap.cps.ncmp.rest.model.DmiErrorMessage;
import org.onap.cps.ncmp.rest.model.DmiErrorMessageDmiResponse;
@@ -84,8 +85,8 @@ public class NetworkCmProxyRestExceptionHandler {
return buildErrorResponse(HttpStatus.BAD_REQUEST, exception);
}
- @ExceptionHandler({AlreadyDefinedException.class})
- public static ResponseEntity<Object> handleAlreadyDefinedExceptions(final Exception exception) {
+ @ExceptionHandler({AlreadyDefinedException.class, PolicyExecutorException.class})
+ public static ResponseEntity<Object> handleConflictExceptions(final Exception exception) {
return buildErrorResponse(HttpStatus.CONFLICT, exception);
}
@@ -113,8 +114,6 @@ public class NetworkCmProxyRestExceptionHandler {
} else {
errorMessage.setDetails(CHECK_LOGS_FOR_DETAILS);
}
- errorMessage.setDetails(
- exception instanceof CpsException ? ((CpsException) exception).getDetails() : CHECK_LOGS_FOR_DETAILS);
return new ResponseEntity<>(errorMessage, status);
}
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 f7d80ad747..43403fa890 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
@@ -32,7 +32,6 @@ import groovy.json.JsonSlurper
import org.mapstruct.factory.Mappers
import org.onap.cps.TestUtils
import org.onap.cps.events.EventsPublisher
-import org.onap.cps.ncmp.api.data.models.CmResourceAddress
import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade
import org.onap.cps.ncmp.api.inventory.models.CompositeState
import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
@@ -41,6 +40,7 @@ import org.onap.cps.ncmp.impl.data.NetworkCmProxyFacade
import org.onap.cps.ncmp.impl.inventory.DataStoreSyncState
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
import org.onap.cps.ncmp.rest.model.DataOperationDefinition
import org.onap.cps.ncmp.rest.model.DataOperationRequest
import org.onap.cps.ncmp.rest.util.CmHandleStateMapper
@@ -94,6 +94,9 @@ class NetworkCmProxyControllerSpec extends Specification {
NetworkCmProxyInventoryFacade mockNetworkCmProxyInventoryFacade = Mock()
@SpringBean
+ AlternateIdMatcher mockAlternateIdMatcher = Mock()
+
+ @SpringBean
ObjectMapper objectMapper = new ObjectMapper()
@SpringBean
@@ -136,12 +139,10 @@ class NetworkCmProxyControllerSpec extends Specification {
def 'Get Resource Data from pass-through operational.'() {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational?resourceIdentifier=parent/child&options=(a=1,b=2)"
- and: 'the expected cm resource address'
- def expectedCmResourceAddress = new CmResourceAddress(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle', 'parent/child')
when: 'get data resource request is performed'
def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
then: 'the NCMP data service is called with correct parameters'
- 1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(expectedCmResourceAddress, '(a=1,b=2)', NO_TOPIC, false, NO_AUTH_HEADER) >> Mono.just(new ResponseEntity<Object>(HttpStatus.OK))
+ 1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(_, '(a=1,b=2)', NO_TOPIC, false, NO_AUTH_HEADER) >> Mono.just(new ResponseEntity<Object>(HttpStatus.OK))
and: 'response status is Ok'
assert response.status == HttpStatus.OK.value()
}
@@ -150,11 +151,10 @@ class NetworkCmProxyControllerSpec extends Specification {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/h123/data/ds/ncmp-datastore:operational?resourceIdentifier=parent/child${additionalUrlParam}"
and: 'the expected cm resource address'
- def expectedCmResourceAddress = new CmResourceAddress('ncmp-datastore:operational', 'h123', 'parent/child')
when: 'get data resource request is performed'
def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
then: 'the NCMP data service is called with correct parameters'
- 1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(expectedCmResourceAddress, NO_OPTIONS, NO_TOPIC, expectedIncludeDescendants, NO_AUTH_HEADER)
+ 1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(_, NO_OPTIONS, NO_TOPIC, expectedIncludeDescendants, NO_AUTH_HEADER)
and: 'response status is OK'
assert response.status == HttpStatus.OK.value()
where: 'the following parameters are used'
@@ -206,8 +206,7 @@ class NetworkCmProxyControllerSpec extends Specification {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=$resourceIdentifier&options=(a=1)"
and: 'ncmp service returns json object'
- def expectedCmResourceAddress = new CmResourceAddress(PASSTHROUGH_RUNNING.datastoreName, 'ch-1', resourceIdentifier)
- 1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(expectedCmResourceAddress, '(a=1)', NO_TOPIC, false, NO_AUTH_HEADER)
+ 1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(_, '(a=1)', NO_TOPIC, false, NO_AUTH_HEADER)
>> new ResponseEntity<Object>('{valid-json}', HttpStatus.OK)
when: 'get data resource request is performed'
def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
@@ -285,17 +284,18 @@ class NetworkCmProxyControllerSpec extends Specification {
assert response.contentAsString == '[{"cmHandle":"ch-1","publicCmHandleProperties":[{"color":"yellow"}],"state":null,"trustLevel":"NONE","moduleSetTag":null,"alternateId":null,"dataProducerIdentifier":null},{"cmHandle":"ch-2","publicCmHandleProperties":[{"color":"green"}],"state":null,"trustLevel":null,"moduleSetTag":"someModuleSetTag","alternateId":"someAlternateId","dataProducerIdentifier":"someDataProducerIdentifier"}]'
}
- def 'Get complete Cm Handle details by Cm Handle id.'() {
- given: 'an endpoint and a cm handle'
- def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/some-cm-handle"
+ def 'Get complete Cm Handle details by Cm Handle Reference.'() {
+ given: 'an endpoint and a cm handle reference'
+ def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/some-cm-handle-reference"
and: 'an existing ncmp service cm handle'
def cmHandleId = 'some-cm-handle'
+ def alternateId = 'some-alternate-id'
def dmiProperties = [prop: 'some DMI property']
def publicProperties = ["public prop": 'some public property']
def compositeState = compositeStateTestObject()
- def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState, currentTrustLevel: TrustLevel.COMPLETE)
- and: 'the service method is invoked with the cm handle id'
- 1 * mockNetworkCmProxyInventoryFacade.getNcmpServiceCmHandle('some-cm-handle') >> ncmpServiceCmHandle
+ def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: alternateId, dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState, currentTrustLevel: TrustLevel.COMPLETE)
+ and: 'the service method is invoked with the cm handle reference'
+ 1 * mockNetworkCmProxyInventoryFacade.getNcmpServiceCmHandle('some-cm-handle-reference') >> ncmpServiceCmHandle
when: 'the cm handle details api is invoked'
def response = mvc.perform(
get(cmHandleDetailsEndpoint)).andReturn().response
@@ -309,13 +309,13 @@ class NetworkCmProxyControllerSpec extends Specification {
assert !response.contentAsString.contains("some DMI property")
}
- def 'Get Cm Handle public properties by Cm Handle id.'() {
+ def 'Get Cm Handle public properties by Cm Handle Reference.'() {
given: 'a cm handle properties endpoint'
- def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/properties"
+ def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle-reference/properties"
and: 'some cm handle public properties'
def publicProperties = ['public prop': 'some public property']
and: 'the service method is invoked with the cm handle id returning the cm handle public properties'
- 1 * mockNetworkCmProxyInventoryFacade.getCmHandlePublicProperties('some-cm-handle') >> publicProperties
+ 1 * mockNetworkCmProxyInventoryFacade.getCmHandlePublicProperties('some-cm-handle-reference') >> publicProperties
when: 'the cm handle properties api is invoked'
def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response
then: 'the correct response is returned'
@@ -324,13 +324,13 @@ class NetworkCmProxyControllerSpec extends Specification {
assertContainsPublicProperties(response)
}
- def 'Get Cm Handle composite state by Cm Handle id.'() {
+ def 'Get Cm Handle composite state by Cm Handle Reference.'() {
given: 'a cm handle state endpoint'
- def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/state"
+ def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle-reference/state"
and: 'some cm handle composite state'
def compositeState = compositeStateTestObject()
and: 'the service method is invoked with the cm handle id returning the cm handle composite state'
- 1 * mockNetworkCmProxyInventoryFacade.getCmHandleCompositeState('some-cm-handle') >> compositeState
+ 1 * mockNetworkCmProxyInventoryFacade.getCmHandleCompositeState('some-cm-handle-reference') >> compositeState
when: 'the cm handle state api is invoked'
def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response
then: 'the correct response is returned'
@@ -416,12 +416,12 @@ class NetworkCmProxyControllerSpec extends Specification {
def 'Getting module definitions filtering on #scenario'() {
when: 'get module definition request is performed'
def response = mvc.perform(
- get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions?module-name=" + moduleName + "&revision=" + revision))
+ get("$ncmpBasePathV1/ch/some-cmhandle-reference/modules/definitions?module-name=" + moduleName + "&revision=" + revision))
.andReturn().response
- then: 'ncmp service method to get definitions by cm handle is invoked when needed'
- numberOfCallsToByCmHandleId * mockNetworkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleId('some-cmhandle') >> []
+ then: 'ncmp service method to get definitions by cm handle reference is invoked when needed'
+ numberOfCallsToByCmHandleId * mockNetworkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleReference('some-cmhandle-reference') >> []
and: 'ncmp service method to get definitions by module is invoked when needed'
- numberOfCallsToByModule * mockNetworkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleAndModule('some-cmhandle', moduleName, revision) >> []
+ numberOfCallsToByModule * mockNetworkCmProxyInventoryFacade.getModuleDefinitionsByCmHandleAndModule('some-cmhandle-reference', moduleName, revision) >> []
and: 'response returns an OK http code'
response.status == HttpStatus.OK.value()
and: 'the correct message is logged when needed'
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy
index 97f3e03c6d..e87acacc74 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy
@@ -29,11 +29,14 @@ import org.onap.cps.ncmp.api.data.exceptions.OperationNotSupportedException
import org.onap.cps.ncmp.api.exceptions.DmiClientRequestException
import org.onap.cps.ncmp.api.exceptions.DmiRequestException
import org.onap.cps.ncmp.api.exceptions.PayloadTooLargeException
+import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException
import org.onap.cps.ncmp.api.exceptions.ServerNcmpException
import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade
import org.onap.cps.ncmp.impl.data.NcmpCachedResourceRequestHandler
import org.onap.cps.ncmp.impl.data.NcmpPassthroughResourceRequestHandler
import org.onap.cps.ncmp.impl.data.NetworkCmProxyFacade
+import org.onap.cps.ncmp.impl.data.policyexecutor.PolicyExecutor
+import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
import org.onap.cps.ncmp.rest.util.CmHandleStateMapper
import org.onap.cps.ncmp.rest.util.DataOperationRequestMapper
import org.onap.cps.ncmp.rest.util.DeprecationHelper
@@ -77,6 +80,9 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
NetworkCmProxyInventoryFacade mockNetworkCmProxyInventoryFacade = Mock()
@SpringBean
+ InventoryPersistence mockInventoryPersistence = Mock()
+
+ @SpringBean
JsonObjectMapper stubbedJsonObjectMapper = Stub()
@SpringBean
@@ -116,25 +122,26 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
dataNodeBaseEndpointNcmpInventory = "$basePathNcmpInventory/v1"
}
- def 'Get request with #scenario exception returns correct HTTP Status with #scenario'() {
+ def 'Get request with #scenario exception returns correct HTTP Status with #scenario exception'() {
when: 'an exception is thrown by the service'
setupTestException(exception, NCMP)
def response = performTestRequest(NCMP)
then: 'an HTTP response is returned with correct message and details'
assertTestResponse(response, expectedErrorCode, expectedErrorMessage, expectedErrorDetails)
where:
- scenario | exception || expectedErrorCode | expectedErrorMessage | expectedErrorDetails
- 'CPS' | new CpsException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | sampleErrorDetails
- 'NCMP-server' | new ServerNcmpException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null
- 'DMI Request' | new DmiRequestException(sampleErrorMessage, sampleErrorDetails) || BAD_REQUEST | sampleErrorMessage | null
- 'Invalid Operation' | new InvalidOperationException('some reason') || BAD_REQUEST | 'some reason' | null
- 'Unsupported Operation' | new OperationNotSupportedException('not yet') || BAD_REQUEST | 'not yet' | null
- 'DataNode Validation' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | null
- 'other' | new IllegalStateException(sampleErrorMessage) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null
- 'Data Node Not Found' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | 'DataNode not found'
- 'Existing entry' | new AlreadyDefinedException('name',null) || CONFLICT | 'Already defined exception' | 'name already exists'
- 'Existing entries' | AlreadyDefinedException.forDataNodes(['A', 'B'], 'myAnchorName') || CONFLICT | 'Already defined exception' | '2 data node(s) already exist'
- 'Operation too large' | new PayloadTooLargeException(sampleErrorMessage) || PAYLOAD_TOO_LARGE | sampleErrorMessage | 'Check logs'
+ scenario | exception || expectedErrorCode | expectedErrorMessage | expectedErrorDetails
+ 'CPS' | new CpsException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | sampleErrorDetails
+ 'NCMP-server' | new ServerNcmpException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null
+ 'DMI Request' | new DmiRequestException(sampleErrorMessage, sampleErrorDetails) || BAD_REQUEST | sampleErrorMessage | null
+ 'Invalid Operation' | new InvalidOperationException('some reason') || BAD_REQUEST | 'some reason' | null
+ 'Unsupported Operation' | new OperationNotSupportedException('not yet') || BAD_REQUEST | 'not yet' | null
+ 'DataNode Validation' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | null
+ 'other' | new IllegalStateException(sampleErrorMessage) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null
+ 'Data Node Not Found' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | 'DataNode not found'
+ 'Existing entry' | new AlreadyDefinedException('name',null) || CONFLICT | 'Already defined exception' | 'name already exists'
+ 'Existing entries' | AlreadyDefinedException.forDataNodes(['A', 'B'], 'myAnchorName') || CONFLICT | 'Already defined exception' | '2 data node(s) already exist'
+ 'Operation too large' | new PayloadTooLargeException(sampleErrorMessage) || PAYLOAD_TOO_LARGE | sampleErrorMessage | 'Check logs'
+ 'Policy Executor' | new PolicyExecutorException(sampleErrorMessage, sampleErrorDetails, null) || CONFLICT | sampleErrorMessage | sampleErrorDetails
}
def 'Post request with exception returns correct HTTP Status.'() {
diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml
index 1f94b34ea6..fda4221f6f 100644
--- a/cps-ncmp-service/pom.xml
+++ b/cps-ncmp-service/pom.xml
@@ -27,7 +27,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
@@ -74,10 +74,6 @@
<artifactId>cps-path-parser</artifactId>
</dependency>
<dependency>
- <groupId>com.google.code.findbugs</groupId>
- <artifactId>annotations</artifactId>
- </dependency>
- <dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
</dependency>
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java
index e93aa4c603..98a343b92e 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java
@@ -20,6 +20,22 @@
package org.onap.cps.ncmp.api.data.models;
-public record CmResourceAddress(String datastoreName, String cmHandleId, String resourceIdentifier) {
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.config.CpsApplicationContext;
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher;
+@Getter
+@RequiredArgsConstructor
+public class CmResourceAddress {
+
+ private final String datastoreName;
+ @Getter(AccessLevel.NONE)
+ private final String cmHandleReference;
+ private final String resourceIdentifier;
+
+ public String getResolvedCmHandleId() {
+ return CpsApplicationContext.getCpsBean(AlternateIdMatcher.class).getCmHandleId(cmHandleReference);
+ }
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobResultService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobResultService.java
new file mode 100644
index 0000000000..c6b7a8bd04
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobResultService.java
@@ -0,0 +1,45 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.datajobs;
+
+/**
+ * Service interface to retrieve the result of a data job.
+ * The operations interact with a DMI Plugin to retrieve data job results.
+ */
+public interface DataJobResultService {
+
+ /**
+ * Retrieves the result of a specific data job.
+ *
+ * @param authorization The authorization header from the REST request.
+ * @param dmiServiceName The name of the DMI Service relevant to the data job.
+ * @param dataProducerId The ID of the producer registered by DMI, used for operations related to this request.
+ * This could include alternate IDs or specific identifiers.
+ * @param dataProducerJobId The identifier of the data producer job within the DMI system.
+ * @param destination The destination of the results: Kafka topic name or S3 bucket name.
+ * @return The result of the data job.
+ */
+ String getDataJobResult(final String authorization,
+ final String dmiServiceName,
+ final String dataProducerId,
+ final String dataProducerJobId,
+ final String destination);
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java
index 6ff79a9344..255b3847eb 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java
@@ -31,20 +31,27 @@ public interface DataJobService {
/**
* process read data job operations.
*
- * @param dataJobId Unique identifier of the job within the request
+ * @param authorization the authorization header from the REST request
+ * @param dataJobId unique identifier of the job within the request
* @param dataJobMetadata data job request headers
* @param dataJobReadRequest read data job request
*/
- void readDataJob(String dataJobId, DataJobMetadata dataJobMetadata, DataJobReadRequest dataJobReadRequest);
+ void readDataJob(String authorization,
+ String dataJobId,
+ DataJobMetadata dataJobMetadata,
+ DataJobReadRequest dataJobReadRequest);
/**
* process write data job operations.
*
- * @param dataJobId Unique identifier of the job within the request
+ * @param authorization the authorization header from the REST request
+ * @param dataJobId unique identifier of the job within the request
* @param dataJobMetadata data job request headers
* @param dataJobWriteRequest write data job request
* @return a list of sub-job write responses
*/
- List<SubJobWriteResponse> writeDataJob(String dataJobId, DataJobMetadata dataJobMetadata,
+ List<SubJobWriteResponse> writeDataJob(String authorization,
+ String dataJobId,
+ DataJobMetadata dataJobMetadata,
DataJobWriteRequest dataJobWriteRequest);
} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobStatusService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobStatusService.java
new file mode 100644
index 0000000000..9cfc49f1d4
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobStatusService.java
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.datajobs;
+
+/**
+ * Service interface to check the status of a data job.
+ * The operations interact with a DMI Plugin to retrieve data job statuses.
+ */
+public interface DataJobStatusService {
+
+ /**
+ * Retrieves the current status of a specific data job.
+ *
+ * @param authorization The authorization header from the REST request.
+ * @param dmiServiceName The name of the DMI Service relevant to the data job.
+ * @param dataProducerId The ID of the producer registered by DMI, used for operations related to this request.
+ * This could include alternate IDs or specific identifiers.
+ * @param dataProducerJobId The identifier of the data producer job within the DMI system.
+ * @return The current status of the data job as a String.
+ */
+ String getDataJobStatus(final String authorization,
+ final String dmiServiceName,
+ final String dataProducerId,
+ final String dataProducerJobId);
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java
index 432b21b8c0..a7a6573279 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java
@@ -23,19 +23,22 @@ package org.onap.cps.ncmp.api.datajobs.models;
import java.util.Collection;
/**
- * Response data for a write operation by the DMI Plugin.
+ * Request data for a write operation by the DMI Plugin.
*
+ * @param destination The destination of the results. ( e.g. S3 Bucket)
* @param dataAcceptType Define the data response accept type.
* e.g. "application/vnd.3gpp.object-tree-hierarchical+json",
* "application/vnd.3gpp.object-tree-flat+json" etc.
* @param dataContentType Define the data request content type.
* e.g. "application/3gpp-json-patch+json" etc.
* @param dataProducerId Identifier of the data producer.
- *
+ * @param dataJobId Identifier for the overall Datajob
* @param data A collection of outgoing write operations.
*/
public record SubJobWriteRequest (
+ String destination,
String dataAcceptType,
String dataContentType,
String dataProducerId,
+ String dataJobId,
Collection<DmiWriteOperation> data) {} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/NcmpException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/NcmpException.java
index 6754965866..3c81d0f536 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/NcmpException.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/NcmpException.java
@@ -39,7 +39,18 @@ public class NcmpException extends RuntimeException {
* @param details the error details
*/
public NcmpException(final String message, final String details) {
- super(message);
+ this(message, details, null);
+ }
+
+ /**
+ * Constructor with cause.
+ *
+ * @param message the error message
+ * @param details the error details
+ * @param cause the cause of the exception
+ */
+ public NcmpException(final String message, final String details, final Throwable cause) {
+ super(message, cause);
this.details = details;
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PolicyExecutorException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PolicyExecutorException.java
new file mode 100644
index 0000000000..bb753b85f1
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PolicyExecutorException.java
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.exceptions;
+
+import lombok.Getter;
+
+/**
+ * Exception to be used when policy execution fails or does not allow to proceed.
+ */
+@Getter
+public class PolicyExecutorException extends NcmpException {
+
+ private static final long serialVersionUID = 6659897770659834798L;
+
+ /**
+ * Constructor to form exception for policy executor responses.
+ *
+ * @param message response message
+ * @param details response details
+ * @param cause the cause of the exception
+ */
+ public PolicyExecutorException(final String message, final String details, final Throwable cause) {
+ super(message, details, cause);
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java
index 794bc238f4..1fa801c3c5 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java
@@ -36,7 +36,6 @@ import org.onap.cps.ncmp.api.inventory.models.CompositeState;
import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration;
import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistrationResponse;
import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle;
-import org.onap.cps.ncmp.api.inventory.models.TrustLevel;
import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService;
import org.onap.cps.ncmp.impl.inventory.CmHandleRegistrationService;
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence;
@@ -44,12 +43,12 @@ import org.onap.cps.ncmp.impl.inventory.ParameterizedCmHandleQueryService;
import org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions;
import org.onap.cps.ncmp.impl.inventory.models.InventoryQueryConditions;
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
-import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelCacheConfig;
+import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager;
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher;
import org.onap.cps.ncmp.impl.utils.YangDataConverter;
import org.onap.cps.spi.model.ModuleDefinition;
import org.onap.cps.spi.model.ModuleReference;
import org.onap.cps.utils.JsonObjectMapper;
-import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Slf4j
@@ -62,9 +61,8 @@ public class NetworkCmProxyInventoryFacade {
private final ParameterizedCmHandleQueryService parameterizedCmHandleQueryService;
private final InventoryPersistence inventoryPersistence;
private final JsonObjectMapper jsonObjectMapper;
-
- @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_CM_HANDLE)
- private final Map<String, TrustLevel> trustLevelPerCmHandle;
+ private final TrustLevelManager trustLevelManager;
+ private final AlternateIdMatcher alternateIdMatcher;
/**
* Registration of Created, Removed, Updated or Upgraded CM Handles.
@@ -72,7 +70,6 @@ public class NetworkCmProxyInventoryFacade {
* @param dmiPluginRegistration Dmi Plugin Registration details
* @return dmiPluginRegistrationResponse
*/
-
public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
final DmiPluginRegistration dmiPluginRegistration) {
return cmHandleRegistrationService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration);
@@ -102,36 +99,39 @@ public class NetworkCmProxyInventoryFacade {
/**
- * Retrieve module references for the given cm handle.
+ * Retrieve module references for the given cm handle reference.
*
- * @param cmHandleId cm handle identifier
+ * @param cmHandleReference cm handle or alternate id identifier
* @return a collection of modules names and revisions
*/
- public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleId) {
+ public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleReference) {
+ final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference);
return inventoryPersistence.getYangResourcesModuleReferences(cmHandleId);
}
/**
* Retrieve module definitions for the given cm handle.
*
- * @param cmHandleId cm handle identifier
+ * @param cmHandleReference cm handle or alternate id identifier
* @return a collection of module definition (moduleName, revision and yang resource content)
*/
- public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(final String cmHandleId) {
+ public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleReference(final String cmHandleReference) {
+ final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference);
return inventoryPersistence.getModuleDefinitionsByCmHandleId(cmHandleId);
}
/**
* Get module definitions for the given parameters.
*
- * @param cmHandleId cm-handle identifier
- * @param moduleName module name
- * @param moduleRevision the revision of the module
+ * @param cmHandleReference cm handle or alternate id identifier
+ * @param moduleName module name
+ * @param moduleRevision the revision of the module
* @return list of module definitions (module name, revision, yang resource content)
*/
- public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleAndModule(final String cmHandleId,
+ public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleAndModule(final String cmHandleReference,
final String moduleName,
final String moduleRevision) {
+ final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference);
return inventoryPersistence.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, moduleRevision);
}
@@ -147,7 +147,7 @@ public class NetworkCmProxyInventoryFacade {
cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class);
validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES);
final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles =
- parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters);
+ parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters);
ncmpServiceCmHandles.forEach(this::applyCurrentTrustLevel);
return ncmpServiceCmHandles;
}
@@ -177,25 +177,27 @@ public class NetworkCmProxyInventoryFacade {
}
/**
- * Retrieve cm handle details for a given cm handle.
+ * Retrieve cm handle details for a given cm handle reference.
*
- * @param cmHandleId cm handle identifier
+ * @param cmHandleReference cm handle or alternate identifier
* @return cm handle details
*/
- public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleId) {
+ public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleReference) {
+ final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference);
final NcmpServiceCmHandle ncmpServiceCmHandle = YangDataConverter.toNcmpServiceCmHandle(
- inventoryPersistence.getYangModelCmHandle(cmHandleId));
+ inventoryPersistence.getYangModelCmHandle(cmHandleId));
applyCurrentTrustLevel(ncmpServiceCmHandle);
return ncmpServiceCmHandle;
}
/**
- * Get cm handle public properties for a given cm handle id.
+ * Get cm handle public properties for a given cm handle or alternate id.
*
- * @param cmHandleId cm handle identifier
+ * @param cmHandleReference cm handle or alternate identifier
* @return cm handle public properties
*/
- public Map<String, String> getCmHandlePublicProperties(final String cmHandleId) {
+ public Map<String, String> getCmHandlePublicProperties(final String cmHandleReference) {
+ final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference);
final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
return YangDataConverter.toPropertiesMap(yangModelCmHandle.getPublicProperties());
}
@@ -203,16 +205,17 @@ public class NetworkCmProxyInventoryFacade {
/**
* Get cm handle composite state for a given cm handle id.
*
- * @param cmHandleId cm handle identifier
+ * @param cmHandleReference cm handle or alternate identifier
* @return cm handle state
*/
- public CompositeState getCmHandleCompositeState(final String cmHandleId) {
+ public CompositeState getCmHandleCompositeState(final String cmHandleReference) {
+ final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference);
return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState();
}
private void applyCurrentTrustLevel(final NcmpServiceCmHandle ncmpServiceCmHandle) {
- ncmpServiceCmHandle.setCurrentTrustLevel(trustLevelPerCmHandle.get(ncmpServiceCmHandle.getCmHandleId()));
+ ncmpServiceCmHandle.setCurrentTrustLevel(trustLevelManager
+ .getEffectiveTrustLevel(ncmpServiceCmHandle.getCmHandleId()));
}
-
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/HttpClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/DmiHttpClientConfig.java
index 583d4bb6ad..8eebb89a0a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/HttpClientConfiguration.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/DmiHttpClientConfig.java
@@ -29,7 +29,7 @@ import org.springframework.context.annotation.Configuration;
@Setter
@Configuration
@ConfigurationProperties(prefix = "ncmp.dmi.httpclient")
-public class HttpClientConfiguration {
+public class DmiHttpClientConfig {
private final DataServices dataServices = new DataServices();
private final ModelServices modelServices = new ModelServices();
@@ -54,19 +54,4 @@ public class HttpClientConfiguration {
private int maximumConnectionsTotal = 10;
private int pendingAcquireMaxCount = 5;
}
-
- /**
- * Base configuration properties for all services.
- */
- @Getter
- @Setter
- public static class ServiceConfig {
- private String connectionProviderName = "cpsConnectionPool";
- private int maximumConnectionsTotal = 100;
- private int pendingAcquireMaxCount = 50;
- private Integer connectionTimeoutInSeconds = 30;
- private long readTimeoutInSeconds = 30;
- private long writeTimeoutInSeconds = 30;
- private int maximumInMemorySizeInMegabytes = 1;
- }
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java
index cff3187966..a6a82b7936 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java
@@ -26,7 +26,11 @@ import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler;
import io.opentelemetry.sdk.trace.samplers.Sampler;
+import jakarta.annotation.PostConstruct;
import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -37,11 +41,14 @@ import org.springframework.http.server.observation.ServerRequestObservationConte
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
+/**
+ * Configuration class for setting up OpenTelemetry tracing in a Spring Boot application.
+ * This class provides beans for OTLP exporters (gRPC and HTTP), a Jaeger remote sampler,
+ * and customizes the ObservationRegistry to exclude certain endpoints from being observed.
+ */
@Configuration
public class OpenTelemetryConfig {
- public static final int JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECOND = 30;
-
@Value("${spring.application.name:cps-application}")
private String serviceId;
@@ -51,9 +58,29 @@ public class OpenTelemetryConfig {
@Value("${cps.tracing.sampler.jaeger_remote.endpoint:http://onap-otel-collector:14250}")
private String jaegerRemoteSamplerUrl;
+ @Value("${cps.tracing.excluded-observation-names:tasks.scheduled.execution}")
+ private String excludedObservationNamesAsCsv;
+
+ private static final int JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECONDS = 30;
+
+ private List<String> excludedObservationNames;
+
/**
- * OTLP Exporter with Grpc exporter protocol.
- */
+ * Initializes the excludedObservationNames after the bean's properties have been set.
+ * This method is called by the Spring container during bean initialization.
+ */
+ @PostConstruct
+ public void init() {
+ excludedObservationNames = Arrays.stream(excludedObservationNamesAsCsv.split(","))
+ .map(String::trim)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Creates an OTLP Exporter with gRPC protocol.
+ *
+ * @return OtlpGrpcSpanExporter bean if tracing is enabled and the exporter protocol is gRPC
+ */
@Bean
@ConditionalOnExpression(
"${cps.tracing.enabled} && 'grpc'.equals('${cps.tracing.exporter.protocol}')")
@@ -62,7 +89,9 @@ public class OpenTelemetryConfig {
}
/**
- * OTLP Exporter with HTTP exporter protocol.
+ * Creates an OTLP Exporter with HTTP protocol.
+ *
+ * @return OtlpHttpSpanExporter bean if tracing is enabled and the exporter protocol is HTTP
*/
@Bean
@ConditionalOnExpression(
@@ -72,39 +101,40 @@ public class OpenTelemetryConfig {
}
/**
- * Jaeger Remote Sampler.
+ * Creates a Jaeger Remote Sampler.
+ *
+ * @return JaegerRemoteSampler bean if tracing is enabled
*/
@Bean
@ConditionalOnProperty("cps.tracing.enabled")
public JaegerRemoteSampler createJaegerRemoteSampler() {
return JaegerRemoteSampler.builder()
- .setEndpoint(jaegerRemoteSamplerUrl)
- .setPollingInterval(Duration.ofSeconds(JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECOND))
- .setInitialSampler(Sampler.alwaysOff())
- .setServiceName(serviceId)
- .build();
+ .setEndpoint(jaegerRemoteSamplerUrl)
+ .setPollingInterval(Duration.ofSeconds(JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECONDS))
+ .setInitialSampler(Sampler.alwaysOff())
+ .setServiceName(serviceId)
+ .build();
}
/**
- * Excluding /actuator/** endpoints.
- */
+ * Customizes the ObservationRegistry to exclude /actuator/** endpoints from being observed.
+ *
+ * @return ObservationRegistryCustomizer bean if tracing is enabled
+ */
@Bean
@ConditionalOnProperty("cps.tracing.enabled")
- ObservationRegistryCustomizer<ObservationRegistry> skipActuatorEndpointsFromObservation() {
+ public ObservationRegistryCustomizer<ObservationRegistry> skipActuatorEndpointsFromObservation() {
final PathMatcher pathMatcher = new AntPathMatcher("/");
return registry ->
- registry.observationConfig().observationPredicate(observationPredicate(pathMatcher));
+ registry.observationConfig().observationPredicate(observationPredicate(pathMatcher));
}
- /**
- * Excluding /actuator/** endpoints.
- */
- static ObservationPredicate observationPredicate(final PathMatcher pathMatcher) {
- return (name, context) -> {
+ private ObservationPredicate observationPredicate(final PathMatcher pathMatcher) {
+ return (observationName, context) -> {
if (context instanceof ServerRequestObservationContext observationContext) {
return !pathMatcher.match("/actuator/**", observationContext.getCarrier().getRequestURI());
} else {
- return true;
+ return !excludedObservationNames.contains(observationName);
}
};
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java
new file mode 100644
index 0000000000..30e7cd5e0b
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.config;
+
+import jakarta.annotation.PostConstruct;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Getter
+@Setter
+@Configuration
+@ConfigurationProperties(prefix = "ncmp.policy-executor.httpclient")
+public class PolicyExecutorHttpClientConfig {
+
+ private final AllServices allServices = new AllServices();
+
+ @Getter
+ @Setter
+ public static class AllServices extends ServiceConfig {
+ private String connectionProviderName = "policyExecutorConfig";
+ }
+
+ @PostConstruct
+ public void increaseReadTimeoutOfWebClient() {
+ allServices.setReadTimeoutInSeconds(allServices.getReadTimeoutInSeconds() + 10);
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/ServiceConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/ServiceConfig.java
new file mode 100644
index 0000000000..f1fce0c7c6
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/ServiceConfig.java
@@ -0,0 +1,36 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.config;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public abstract class ServiceConfig {
+ private String connectionProviderName = "";
+ private int maximumInMemorySizeInMegabytes = 1;
+ private int maximumConnectionsTotal = 1;
+ private int pendingAcquireMaxCount = 1;
+ private Integer connectionTimeoutInSeconds = 1;
+ private long readTimeoutInSeconds = 1;
+ private long writeTimeoutInSeconds = 1;
+}
diff --git a/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfig.java
index 418de9b17a..109a541cb3 100644
--- a/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfig.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.cache;
+package org.onap.cps.ncmp.impl.cache;
import com.hazelcast.config.Config;
import com.hazelcast.config.MapConfig;
@@ -40,19 +40,30 @@ public class HazelcastCacheConfig {
@Value("${hazelcast.cluster-name}")
protected String clusterName;
+ @Value("${hazelcast.instance-config-name}")
+ protected String instanceConfigName;
+
@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) {
- return Hazelcast.newHazelcastInstance(initializeConfig(hazelcastInstanceName, namedConfig));
+ protected HazelcastInstance getOrCreateHazelcastInstance(final NamedConfig namedConfig) {
+ return Hazelcast.getOrCreateHazelcastInstance(defineInstanceConfig(instanceConfigName, namedConfig));
+ }
+
+ private Config defineInstanceConfig(final String instanceConfigName, final NamedConfig namedConfig) {
+ final Config config = getHazelcastInstanceConfig(instanceConfigName);
+ config.setClusterName(clusterName);
+ config.setClassLoader(org.onap.cps.spi.model.Dataspace.class.getClassLoader());
+ configureDataStructures(namedConfig, config);
+ exposeClusterInformation(config);
+ updateDiscoveryMode(config);
+ return config;
}
- private Config initializeConfig(final String instanceName, final NamedConfig namedConfig) {
- final Config config = new Config(instanceName);
+ private static void configureDataStructures(final NamedConfig namedConfig, final Config config) {
if (namedConfig instanceof MapConfig) {
config.addMapConfig((MapConfig) namedConfig);
}
@@ -62,11 +73,16 @@ public class HazelcastCacheConfig {
if (namedConfig instanceof SetConfig) {
config.addSetConfig((SetConfig) namedConfig);
}
+ }
- config.setClusterName(clusterName);
- config.setClassLoader(org.onap.cps.spi.model.Dataspace.class.getClassLoader());
- exposeClusterInformation(config);
- updateDiscoveryMode(config);
+ private Config getHazelcastInstanceConfig(final String instanceConfigName) {
+ final HazelcastInstance hazelcastInstance = Hazelcast.getHazelcastInstanceByName(instanceConfigName);
+ Config config = null;
+ if (hazelcastInstance != null) {
+ config = hazelcastInstance.getConfig();
+ } else {
+ config = new Config(instanceConfigName);
+ }
return config;
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java
index a4f9be357f..cb5102063e 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java
@@ -23,7 +23,7 @@ package org.onap.cps.ncmp.impl.cmnotificationsubscription.cache;
import com.hazelcast.config.MapConfig;
import com.hazelcast.map.IMap;
import java.util.Map;
-import org.onap.cps.cache.HazelcastCacheConfig;
+import org.onap.cps.ncmp.impl.cache.HazelcastCacheConfig;
import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -43,7 +43,7 @@ public class CmSubscriptionConfig extends HazelcastCacheConfig {
*/
@Bean
public IMap<String, Map<String, DmiCmSubscriptionDetails>> cmNotificationSubscriptionCache() {
- return createHazelcastInstance("hazelCastInstanceCmNotificationSubscription",
- cmNotificationSubscriptionCacheMapConfig).getMap("cmNotificationSubscriptionCache");
+ return getOrCreateHazelcastInstance(cmNotificationSubscriptionCacheMapConfig).getMap(
+ "cmNotificationSubscriptionCache");
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandler.java
index c5052f1405..b5ab7f6525 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandler.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandler.java
@@ -50,7 +50,7 @@ public class DmiCacheHandler {
private final InventoryPersistence inventoryPersistence;
/**
- * Adds new subscription to the subscription cache.
+ * Adds subscription to the subscription cache.
*
* @param subscriptionId subscription id
* @param predicates subscription request predicates
@@ -60,6 +60,18 @@ public class DmiCacheHandler {
}
/**
+ * Adds subscription to the subscription cache.
+ *
+ * @param subscriptionId subscription id
+ * @param dmiCmSubscriptionDetailsPerDmi map of dmi cm notification subscription details per dmi
+ */
+ public void add(final String subscriptionId,
+ final Map<String, DmiCmSubscriptionDetails>
+ dmiCmSubscriptionDetailsPerDmi) {
+ cmNotificationSubscriptionCache.put(subscriptionId, dmiCmSubscriptionDetailsPerDmi);
+ }
+
+ /**
* Get cm notification subscription cache entry via subscription id.
*
* @param subscriptionId subscription id
@@ -122,8 +134,8 @@ public class DmiCacheHandler {
* @param status String of status
*
*/
- public void updateDmiSubscriptionStatusPerDmi(final String subscriptionId, final String dmiServiceName,
- final CmSubscriptionStatus status) {
+ public void updateDmiSubscriptionStatus(final String subscriptionId, final String dmiServiceName,
+ final CmSubscriptionStatus status) {
final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi =
cmNotificationSubscriptionCache.get(subscriptionId);
dmiSubscriptionsPerDmi.get(dmiServiceName).setCmSubscriptionStatus(status);
@@ -162,7 +174,7 @@ public class DmiCacheHandler {
* @param dmiServiceName String of dmiServiceName
*
*/
- public void removeFromDatabasePerDmi(final String subscriptionId, final String dmiServiceName) {
+ public void removeFromDatabase(final String subscriptionId, final String dmiServiceName) {
final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates =
cmNotificationSubscriptionCache.get(subscriptionId).get(dmiServiceName)
.getDmiCmSubscriptionPredicates();
@@ -210,6 +222,6 @@ public class DmiCacheHandler {
private boolean isAcceptedOrRejected(final DmiCmSubscriptionDetails dmiCmSubscription) {
return dmiCmSubscription.getCmSubscriptionStatus().toString().equals("ACCEPTED")
- || dmiCmSubscription.getCmSubscriptionStatus().toString().equals("REJECTED");
+ || dmiCmSubscription.getCmSubscriptionStatus().toString().equals("REJECTED");
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumer.java
index 0207fb90e3..9e90eabbc4 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumer.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumer.java
@@ -21,8 +21,6 @@
package org.onap.cps.ncmp.impl.cmnotificationsubscription.cmavc;
import io.cloudevents.CloudEvent;
-import io.cloudevents.core.builder.CloudEventBuilder;
-import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
@@ -56,11 +54,8 @@ public class CmAvcEventConsumer {
containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory")
public void consumeAndForward(
final ConsumerRecord<String, CloudEvent> cmAvcEventAsConsumerRecord) {
- log.debug("Consuming AVC event {} ...", cmAvcEventAsConsumerRecord.value());
- final String newEventId = UUID.randomUUID().toString();
- final CloudEvent outgoingAvcEvent =
- CloudEventBuilder.from(cmAvcEventAsConsumerRecord.value()).withId(newEventId)
- .build();
- eventsPublisher.publishCloudEvent(cmEventsTopicName, newEventId, outgoingAvcEvent);
+ final CloudEvent outgoingAvcEvent = cmAvcEventAsConsumerRecord.value();
+ log.debug("Consuming AVC event {} ...", outgoingAvcEvent);
+ eventsPublisher.publishCloudEvent(cmEventsTopicName, outgoingAvcEvent.getId(), outgoingAvcEvent);
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java
new file mode 100644
index 0000000000..423c603a92
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java
@@ -0,0 +1,103 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi;
+
+import static org.onap.cps.ncmp.api.data.models.DatastoreType.fromDatastoreName;
+import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.PENDING;
+
+import java.util.ArrayList;
+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.Set;
+import java.util.stream.Collectors;
+import org.onap.cps.ncmp.api.data.models.DatastoreType;
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails;
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionKey;
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DmiCmSubscriptionDetailsPerDmiMapper {
+
+ /**
+ * Maps Dmi Subscription Keys grouped by Dmi Plugin to DmiCmSubscriptionDetails per Dmi plugin.
+ *
+ * @param subscribersPerDmi Details managed by each dmi plugin
+ * @return Grouped Dmi Subscription details per dmi plugin
+ */
+ public Map<String, DmiCmSubscriptionDetails> toDmiCmSubscriptionsPerDmi(
+ final Map<String, Collection<DmiCmSubscriptionKey>> subscribersPerDmi) {
+
+ final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi = new HashMap<>();
+
+ subscribersPerDmi.forEach((dmiPluginName, dmiCmSubscriptionKeys) -> {
+ final Map<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> groupedByDatastoreTypeAndXpath =
+ groupByDatastoreTypeAndXpath(dmiCmSubscriptionKeys);
+
+ final List<DmiCmSubscriptionPredicate> dmiSubscriptionPredicates =
+ createDmiCmSubscriptionPredicates(groupedByDatastoreTypeAndXpath);
+
+ final DmiCmSubscriptionDetails dmiCmSubscriptionDetails =
+ new DmiCmSubscriptionDetails(dmiSubscriptionPredicates, PENDING);
+
+ dmiSubscriptionsPerDmi.put(dmiPluginName, dmiCmSubscriptionDetails);
+ });
+
+ return dmiSubscriptionsPerDmi;
+ }
+
+ private static Map<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> groupByDatastoreTypeAndXpath(
+ final Collection<DmiCmSubscriptionKey> dmiCmSubscriptionKeys) {
+ return dmiCmSubscriptionKeys.stream().collect(Collectors.groupingBy(
+ datastoreTypeAndXpath -> new DatastoreTypeAndXpath(
+ fromDatastoreName(datastoreTypeAndXpath.datastoreName()), datastoreTypeAndXpath.xpath())));
+ }
+
+ private static List<DmiCmSubscriptionPredicate> createDmiCmSubscriptionPredicates(
+ final Map<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> groupedByDatastoreTypeAndXpath) {
+ final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates = new ArrayList<>();
+
+ for (final Map.Entry<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> datastoreTypeXpathGroupEntry :
+ groupedByDatastoreTypeAndXpath.entrySet()) {
+ final DatastoreTypeAndXpath datastoreTypeAndXpath = datastoreTypeXpathGroupEntry.getKey();
+ final Set<String> cmHandleIds = new HashSet<>();
+
+ for (final DmiCmSubscriptionKey dmiCmSubscriptionKey : datastoreTypeXpathGroupEntry.getValue()) {
+ cmHandleIds.add(dmiCmSubscriptionKey.cmHandleId());
+ }
+
+ final Set<String> xpaths = Collections.singleton(datastoreTypeAndXpath.xpath());
+ dmiCmSubscriptionPredicates.add(
+ new DmiCmSubscriptionPredicate(cmHandleIds, datastoreTypeAndXpath.datastoreType(), xpaths));
+ }
+
+ return dmiCmSubscriptionPredicates;
+ }
+
+
+ private record DatastoreTypeAndXpath(DatastoreType datastoreType, String xpath) { }
+
+}
+
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumer.java
index 20ccf528ed..20c7c7b13d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumer.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumer.java
@@ -80,7 +80,7 @@ public class DmiOutEventConsumer {
dmiCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName);
}
if (eventType.equals("subscriptionDeleteResponse")) {
- dmiCacheHandler.removeFromDatabasePerDmi(subscriptionId, dmiPluginName);
+ dmiCacheHandler.removeFromDatabase(subscriptionId, dmiPluginName);
}
handleEventsStatusPerDmi(subscriptionId, eventType);
}
@@ -96,7 +96,7 @@ public class DmiOutEventConsumer {
private void handleCacheStatusPerDmi(final String subscriptionId, final String dmiPluginName,
final CmSubscriptionStatus cmSubscriptionStatus) {
- dmiCacheHandler.updateDmiSubscriptionStatusPerDmi(subscriptionId, dmiPluginName,
+ dmiCacheHandler.updateDmiSubscriptionStatus(subscriptionId, dmiPluginName,
cmSubscriptionStatus);
}
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiOperationCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionKey.java
index 5cb24bc398..edc3c566be 100644
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiOperationCmHandle.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionKey.java
@@ -1,6 +1,6 @@
/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,20 +15,16 @@
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
+ * ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.dmi.rest.stub.model.data.operational;
+package org.onap.cps.ncmp.impl.cmnotificationsubscription.models;
-import java.util.HashMap;
-import java.util.Map;
-import lombok.Getter;
-import lombok.Setter;
-
-@Setter
-@Getter
-public class DmiOperationCmHandle {
- private String id;
- private Map<String, String> cmHandleProperties = new HashMap<>();
- private String moduleSetTag;
-}
+/**
+ * Key used to find the records to be sent to the DMI plugin.
+ *
+ * @param datastoreName datastore name
+ * @param cmHandleId cmhandle id
+ * @param xpath xpath
+ */
+public record DmiCmSubscriptionKey(String datastoreName, String cmHandleId, String xpath) { }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionTuple.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionTuple.java
new file mode 100644
index 0000000000..cd4a15af47
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionTuple.java
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.cmnotificationsubscription.models;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Tuple to be used during for to delete usecase.
+ *
+ * @param lastRemainingSubscriptionsPerDmi subscriptions that are used by only one subscriber grouped per dmi
+ * @param overlappingSubscriptionsPerDmi subscriptions that are shared by multiple subscribers grouped per dmi
+ */
+public record DmiCmSubscriptionTuple(Map<String, Collection<DmiCmSubscriptionKey>> lastRemainingSubscriptionsPerDmi,
+ Map<String, Collection<DmiCmSubscriptionKey>> overlappingSubscriptionsPerDmi) {
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandler.java
index 3a9b2066b2..90c5c575e6 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandler.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandler.java
@@ -37,8 +37,7 @@ public interface CmSubscriptionHandler {
* Process cm notification subscription delete request.
*
* @param subscriptionId subscription id
- * @param predicates subscription predicates
*/
- void processSubscriptionDeleteRequest(final String subscriptionId, final List<Predicate> predicates);
+ void processSubscriptionDeleteRequest(final String subscriptionId);
} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImpl.java
index 9d33d25816..1cdc7ed3e0 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImpl.java
@@ -21,34 +21,50 @@
package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler;
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiCmSubscriptionDetailsPerDmiMapper;
import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiInEventMapper;
import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiInEventProducer;
import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus;
import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails;
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionKey;
import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate;
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionTuple;
import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmSubscriptionPersistenceService;
import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.Predicate;
import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent;
import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.DmiInEvent;
+import org.onap.cps.ncmp.impl.inventory.InventoryPersistence;
+import org.onap.cps.spi.model.DataNode;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CmSubscriptionHandlerImpl implements CmSubscriptionHandler {
+ private static final Pattern SUBSCRIPTION_KEY_FROM_XPATH_PATTERN = Pattern.compile(
+ "^/datastores/datastore\\[@name='([^']*)']/cm-handles/cm-handle\\[@id='([^']*)']/"
+ + "filters/filter\\[@xpath='(.*)']$");
+
private final CmSubscriptionPersistenceService cmSubscriptionPersistenceService;
private final CmSubscriptionComparator cmSubscriptionComparator;
private final NcmpOutEventMapper ncmpOutEventMapper;
private final DmiInEventMapper dmiInEventMapper;
+ private final DmiCmSubscriptionDetailsPerDmiMapper dmiCmSubscriptionDetailsPerDmiMapper;
private final NcmpOutEventProducer ncmpOutEventProducer;
private final DmiInEventProducer dmiInEventProducer;
private final DmiCacheHandler dmiCacheHandler;
+ private final InventoryPersistence inventoryPersistence;
@Override
public void processSubscriptionCreateRequest(final String subscriptionId, final List<Predicate> predicates) {
@@ -62,10 +78,45 @@ public class CmSubscriptionHandlerImpl implements CmSubscriptionHandler {
}
@Override
- public void processSubscriptionDeleteRequest(final String subscriptionId, final List<Predicate> predicates) {
- dmiCacheHandler.add(subscriptionId, predicates);
- sendSubscriptionDeleteRequestToDmi(subscriptionId);
- scheduleNcmpOutEventResponse(subscriptionId, "subscriptionDeleteResponse");
+ public void processSubscriptionDeleteRequest(final String subscriptionId) {
+ final Collection<DataNode> subscriptionDataNodes =
+ cmSubscriptionPersistenceService.getAllNodesForSubscriptionId(subscriptionId);
+ final DmiCmSubscriptionTuple dmiCmSubscriptionTuple =
+ getLastRemainingAndOverlappingSubscriptionsPerDmi(subscriptionDataNodes);
+ dmiCacheHandler.add(subscriptionId, mergeDmiCmSubscriptionDetailsPerDmiMaps(dmiCmSubscriptionTuple));
+ if (dmiCmSubscriptionTuple.lastRemainingSubscriptionsPerDmi().isEmpty()) {
+ acceptAndPublishDeleteRequest(subscriptionId);
+ } else {
+ sendSubscriptionDeleteRequestToDmi(subscriptionId,
+ dmiCmSubscriptionDetailsPerDmiMapper.toDmiCmSubscriptionsPerDmi(
+ dmiCmSubscriptionTuple.lastRemainingSubscriptionsPerDmi()));
+ scheduleNcmpOutEventResponse(subscriptionId, "subscriptionDeleteResponse");
+ }
+ }
+
+ private Map<String, DmiCmSubscriptionDetails> mergeDmiCmSubscriptionDetailsPerDmiMaps(
+ final DmiCmSubscriptionTuple dmiCmSubscriptionTuple) {
+ final Map<String, DmiCmSubscriptionDetails> lastRemainingDmiSubscriptionsPerDmi =
+ dmiCmSubscriptionDetailsPerDmiMapper.toDmiCmSubscriptionsPerDmi(
+ dmiCmSubscriptionTuple.lastRemainingSubscriptionsPerDmi());
+ final Map<String, DmiCmSubscriptionDetails> overlappingDmiSubscriptionsPerDmi =
+ dmiCmSubscriptionDetailsPerDmiMapper.toDmiCmSubscriptionsPerDmi(
+ dmiCmSubscriptionTuple.overlappingSubscriptionsPerDmi());
+ final Map<String, DmiCmSubscriptionDetails> mergedDmiSubscriptionsPerDmi =
+ new HashMap<>(lastRemainingDmiSubscriptionsPerDmi);
+ overlappingDmiSubscriptionsPerDmi.forEach((dmiServiceName, dmiCmSubscriptionDetails) ->
+ mergedDmiSubscriptionsPerDmi.merge(dmiServiceName, dmiCmSubscriptionDetails,
+ this::mergeDmiCmSubscriptionDetails));
+ return mergedDmiSubscriptionsPerDmi;
+ }
+
+ private DmiCmSubscriptionDetails mergeDmiCmSubscriptionDetails(
+ final DmiCmSubscriptionDetails dmiCmSubscriptionDetails,
+ final DmiCmSubscriptionDetails otherDmiCmSubscriptionDetails) {
+ final List<DmiCmSubscriptionPredicate> mergedDmiCmSubscriptionPredicates =
+ new ArrayList<>(dmiCmSubscriptionDetails.getDmiCmSubscriptionPredicates());
+ mergedDmiCmSubscriptionPredicates.addAll(otherDmiCmSubscriptionDetails.getDmiCmSubscriptionPredicates());
+ return new DmiCmSubscriptionDetails(mergedDmiCmSubscriptionPredicates, CmSubscriptionStatus.PENDING);
}
private void scheduleNcmpOutEventResponse(final String subscriptionId, final String eventType) {
@@ -81,6 +132,19 @@ public class CmSubscriptionHandlerImpl implements CmSubscriptionHandler {
ncmpOutEventProducer.publishNcmpOutEvent(subscriptionId, "subscriptionCreateResponse", ncmpOutEvent, false);
}
+ private void acceptAndPublishDeleteRequest(final String subscriptionId) {
+ final Set<String> dmiServiceNames = dmiCacheHandler.get(subscriptionId).keySet();
+ for (final String dmiServiceName : dmiServiceNames) {
+ dmiCacheHandler.updateDmiSubscriptionStatus(subscriptionId, dmiServiceName,
+ CmSubscriptionStatus.ACCEPTED);
+ dmiCacheHandler.removeFromDatabase(subscriptionId, dmiServiceName);
+ }
+ final NcmpOutEvent ncmpOutEvent = ncmpOutEventMapper.toNcmpOutEvent(subscriptionId,
+ dmiCacheHandler.get(subscriptionId));
+ ncmpOutEventProducer.publishNcmpOutEvent(subscriptionId, "subscriptionDeleteResponse", ncmpOutEvent,
+ false);
+ }
+
private void handleNewCmSubscription(final String subscriptionId) {
final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi =
dmiCacheHandler.get(subscriptionId);
@@ -90,7 +154,7 @@ public class CmSubscriptionHandlerImpl implements CmSubscriptionHandler {
dmiSubscriptionDetails.getDmiCmSubscriptionPredicates());
if (dmiCmSubscriptionPredicates.isEmpty()) {
- acceptAndPublishNcmpOutEventPerDmi(subscriptionId, dmiPluginName);
+ acceptAndPersistCmSubscriptionPerDmi(subscriptionId, dmiPluginName);
} else {
publishDmiInEventPerDmi(subscriptionId, dmiPluginName, dmiCmSubscriptionPredicates);
}
@@ -98,26 +162,68 @@ public class CmSubscriptionHandlerImpl implements CmSubscriptionHandler {
}
private void publishDmiInEventPerDmi(final String subscriptionId, final String dmiPluginName,
- final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates) {
+ final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates) {
final DmiInEvent dmiInEvent = dmiInEventMapper.toDmiInEvent(dmiCmSubscriptionPredicates);
dmiInEventProducer.publishDmiInEvent(subscriptionId, dmiPluginName,
"subscriptionCreateRequest", dmiInEvent);
}
- private void acceptAndPublishNcmpOutEventPerDmi(final String subscriptionId, final String dmiPluginName) {
- dmiCacheHandler.updateDmiSubscriptionStatusPerDmi(subscriptionId, dmiPluginName,
+ private void acceptAndPersistCmSubscriptionPerDmi(final String subscriptionId, final String dmiPluginName) {
+ dmiCacheHandler.updateDmiSubscriptionStatus(subscriptionId, dmiPluginName,
CmSubscriptionStatus.ACCEPTED);
dmiCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName);
}
- private void sendSubscriptionDeleteRequestToDmi(final String subscriptionId) {
- final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi =
- dmiCacheHandler.get(subscriptionId);
- dmiSubscriptionsPerDmi.forEach((dmiPluginName, dmiSubscriptionDetails) -> {
- final DmiInEvent dmiInEvent = dmiInEventMapper.toDmiInEvent(
- dmiSubscriptionDetails.getDmiCmSubscriptionPredicates());
- dmiInEventProducer.publishDmiInEvent(subscriptionId, dmiPluginName,
- "subscriptionDeleteRequest", dmiInEvent);
+ private void sendSubscriptionDeleteRequestToDmi(final String subscriptionId,
+ final Map<String, DmiCmSubscriptionDetails>
+ dmiCmSubscriptionsPerDmi) {
+ dmiCmSubscriptionsPerDmi.forEach((dmiPluginName, dmiCmSubscriptionDetails) -> {
+ final DmiInEvent dmiInEvent =
+ dmiInEventMapper.toDmiInEvent(
+ dmiCmSubscriptionDetails.getDmiCmSubscriptionPredicates());
+ dmiInEventProducer.publishDmiInEvent(subscriptionId,
+ dmiPluginName, "subscriptionDeleteRequest", dmiInEvent);
});
}
+
+
+ private DmiCmSubscriptionTuple getLastRemainingAndOverlappingSubscriptionsPerDmi(
+ final Collection<DataNode> subscriptionNodes) {
+ final Map<String, Collection<DmiCmSubscriptionKey>> lastRemainingSubscriptionsPerDmi = new HashMap<>();
+ final Map<String, Collection<DmiCmSubscriptionKey>> overlappingSubscriptionsPerDmi = new HashMap<>();
+
+ for (final DataNode subscriptionNode : subscriptionNodes) {
+ final DmiCmSubscriptionKey dmiCmSubscriptionKey = extractCmSubscriptionKey(subscriptionNode.getXpath());
+ final String dmiServiceName = inventoryPersistence.getYangModelCmHandle(
+ dmiCmSubscriptionKey.cmHandleId()).getDmiServiceName();
+ final List<String> subscribers = (List<String>) subscriptionNode.getLeaves().get("subscriptionIds");
+ populateDmiCmSubscriptionTuple(subscribers, overlappingSubscriptionsPerDmi,
+ lastRemainingSubscriptionsPerDmi, dmiServiceName, dmiCmSubscriptionKey);
+ }
+ return new DmiCmSubscriptionTuple(lastRemainingSubscriptionsPerDmi, overlappingSubscriptionsPerDmi);
+ }
+
+ private static void populateDmiCmSubscriptionTuple(final List<String> subscribers,
+ final Map<String, Collection<DmiCmSubscriptionKey>>
+ overlappingSubscriptionsPerDmi,
+ final Map<String, Collection<DmiCmSubscriptionKey>>
+ lastRemainingSubscriptionsPerDmi,
+ final String dmiServiceName,
+ final DmiCmSubscriptionKey dmiCmSubscriptionKey) {
+ final Map<String, Collection<DmiCmSubscriptionKey>> targetMap =
+ subscribers.size() > 1 ? overlappingSubscriptionsPerDmi : lastRemainingSubscriptionsPerDmi;
+ targetMap.computeIfAbsent(dmiServiceName, dmiName -> new HashSet<>()).add(dmiCmSubscriptionKey);
+ }
+
+ private DmiCmSubscriptionKey extractCmSubscriptionKey(final String xpath) {
+ final Matcher matcher = SUBSCRIPTION_KEY_FROM_XPATH_PATTERN.matcher(xpath);
+ if (matcher.find()) {
+ final String datastoreName = matcher.group(1);
+ final String cmHandleId = matcher.group(2);
+ final String filterXpath = matcher.group(3);
+ return new DmiCmSubscriptionKey(datastoreName, cmHandleId, filterXpath);
+ }
+ throw new IllegalArgumentException("DataNode xpath does not represent a subscription key");
+ }
+
} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumer.java
index 1e1359dd0d..cba64e0e94 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumer.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumer.java
@@ -63,7 +63,7 @@ public class NcmpInEventConsumer {
if ("subscriptionDeleteRequest".equals(cloudEvent.getType())) {
log.info("Subscription delete request for source {} with subscription id {} ...",
cloudEvent.getSource(), subscriptionId);
- cmSubscriptionHandler.processSubscriptionDeleteRequest(subscriptionId, predicates);
+ cmSubscriptionHandler.processSubscriptionDeleteRequest(subscriptionId);
}
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapper.java
index ffd4b014fb..afff9d1298 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapper.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapper.java
@@ -21,6 +21,8 @@
package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
@@ -76,9 +78,9 @@ public class NcmpOutEventMapper {
final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi,
final Data cmSubscriptionData) {
- final List<String> acceptedCmHandleIds = new ArrayList<>();
- final List<String> pendingCmHandleIds = new ArrayList<>();
- final List<String> rejectedCmHandleIds = new ArrayList<>();
+ final Collection<String> acceptedCmHandleIds = new HashSet<>();
+ final Collection<String> pendingCmHandleIds = new HashSet<>();
+ final Collection<String> rejectedCmHandleIds = new HashSet<>();
dmiSubscriptionsPerDmi.forEach((dmiPluginName, dmiSubscriptionDetails) -> {
final CmSubscriptionStatus cmSubscriptionStatus =
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducer.java
index 01d720937f..3371d59f7a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducer.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducer.java
@@ -57,7 +57,8 @@ public class NcmpOutEventProducer {
private final NcmpOutEventMapper ncmpOutEventMapper;
private final DmiCacheHandler dmiCacheHandler;
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
- private static final Map<String, ScheduledFuture<?>> scheduledTasksPerSubscriptionId = new ConcurrentHashMap<>();
+ private static final Map<String, ScheduledFuture<?>> scheduledTasksPerSubscriptionIdAndEventType =
+ new ConcurrentHashMap<>();
/**
* Publish the event to the client who requested the subscription with key as subscription id and event is Cloud
@@ -73,14 +74,20 @@ public class NcmpOutEventProducer {
public void publishNcmpOutEvent(final String subscriptionId, final String eventType,
final NcmpOutEvent ncmpOutEvent, final boolean isScheduledEvent) {
- if (isScheduledEvent && !scheduledTasksPerSubscriptionId.containsKey(subscriptionId)) {
+ final String taskKey = subscriptionId.concat(eventType);
+
+ if (isScheduledEvent && !scheduledTasksPerSubscriptionIdAndEventType.containsKey(taskKey)) {
final ScheduledFuture<?> scheduledFuture = scheduleAndPublishNcmpOutEvent(subscriptionId, eventType);
- scheduledTasksPerSubscriptionId.putIfAbsent(subscriptionId, scheduledFuture);
- log.debug("Scheduled the CmNotificationSubscriptionEvent for subscriptionId : {}", subscriptionId);
+ scheduledTasksPerSubscriptionIdAndEventType.putIfAbsent(taskKey, scheduledFuture);
+ log.debug("Scheduled the Cm Subscription Event for subscriptionId : {} and eventType : {}", subscriptionId,
+ eventType);
} else {
- cancelScheduledTaskForSubscriptionId(subscriptionId);
- publishNcmpOutEventNow(subscriptionId, eventType, ncmpOutEvent);
- log.info("Published CmNotificationSubscriptionEvent on demand for subscriptionId : {}", subscriptionId);
+ cancelScheduledTask(taskKey);
+ if (ncmpOutEvent != null) {
+ publishNcmpOutEventNow(subscriptionId, eventType, ncmpOutEvent);
+ log.debug("Published Cm Subscription Event on demand for subscriptionId : {} and eventType : {}",
+ subscriptionId, eventType);
+ }
}
}
@@ -92,12 +99,12 @@ public class NcmpOutEventProducer {
TimeUnit.MILLISECONDS);
}
- private void cancelScheduledTaskForSubscriptionId(final String subscriptionId) {
+ private void cancelScheduledTask(final String taskKey) {
- final ScheduledFuture<?> scheduledFuture = scheduledTasksPerSubscriptionId.get(subscriptionId);
+ final ScheduledFuture<?> scheduledFuture = scheduledTasksPerSubscriptionIdAndEventType.get(taskKey);
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
- scheduledTasksPerSubscriptionId.remove(subscriptionId);
+ scheduledTasksPerSubscriptionIdAndEventType.remove(taskKey);
}
}
@@ -106,10 +113,8 @@ public class NcmpOutEventProducer {
private void publishNcmpOutEventNow(final String subscriptionId, final String eventType,
final NcmpOutEvent ncmpOutEvent) {
final CloudEvent ncmpOutEventAsCloudEvent =
- buildAndGetNcmpOutEventAsCloudEvent(jsonObjectMapper, subscriptionId, eventType,
- ncmpOutEvent);
- eventsPublisher.publishCloudEvent(ncmpOutEventTopic, subscriptionId,
- ncmpOutEventAsCloudEvent);
+ buildAndGetNcmpOutEventAsCloudEvent(jsonObjectMapper, subscriptionId, eventType, ncmpOutEvent);
+ eventsPublisher.publishCloudEvent(ncmpOutEventTopic, subscriptionId, ncmpOutEventAsCloudEvent);
dmiCacheHandler.removeAcceptedAndRejectedDmiSubscriptionEntries(subscriptionId);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java
index c24507a1a7..6b5ed908b8 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2024 Nordix Foundation
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -159,6 +160,18 @@ public class CmSubscriptionPersistenceService {
}
}
+ /**
+ * Retrieve all existing dataNodes for given subscription id.
+ *
+ * @param subscriptionId subscription id
+ * @return collection of DataNodes
+ */
+ public Collection<DataNode> getAllNodesForSubscriptionId(final String subscriptionId) {
+ return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted(subscriptionId),
+ OMIT_DESCENDANTS);
+ }
+
private void deleteListOfSubscriptionsFor(final DatastoreType datastoreType, final String cmHandleId,
final String xpath) {
cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
@@ -204,7 +217,7 @@ public class CmSubscriptionPersistenceService {
cpsDataService.saveListElements(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
datastoreType.getDatastoreName(), cmHandleId), subscriptionDetailsAsJson,
- OffsetDateTime.now());
+ OffsetDateTime.now(), ContentType.JSON);
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
index 4cbf9d4b3b..301b8195e4 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
@@ -41,15 +41,16 @@ import org.onap.cps.ncmp.api.exceptions.DmiClientRequestException;
import org.onap.cps.ncmp.impl.data.models.DmiDataOperation;
import org.onap.cps.ncmp.impl.data.models.DmiDataOperationRequest;
import org.onap.cps.ncmp.impl.data.models.DmiOperationCmHandle;
+import org.onap.cps.ncmp.impl.data.policyexecutor.PolicyExecutor;
import org.onap.cps.ncmp.impl.data.utils.DmiDataOperationsHelper;
import org.onap.cps.ncmp.impl.dmi.DmiProperties;
import org.onap.cps.ncmp.impl.dmi.DmiRestClient;
-import org.onap.cps.ncmp.impl.dmi.DmiServiceUrlTemplateBuilder;
-import org.onap.cps.ncmp.impl.dmi.UrlTemplateParameters;
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence;
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState;
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
import org.onap.cps.ncmp.impl.models.DmiRequestBody;
+import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
import org.onap.cps.spi.exceptions.CpsException;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.http.ResponseEntity;
@@ -92,12 +93,12 @@ public class DmiDataOperations {
final String topic,
final String requestId,
final String authorization) {
- final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.cmHandleId());
+ final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.getResolvedCmHandleId());
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(cmResourceAddress
- .datastoreName(), yangModelCmHandle, cmResourceAddress.resourceIdentifier(), options, topic);
+ .getDatastoreName(), yangModelCmHandle, cmResourceAddress.getResourceIdentifier(), options, topic);
return dmiRestClient.asynchronousPostOperationWithJsonData(DATA, urlTemplateParameters, jsonRequestBody, READ,
authorization);
}
@@ -138,8 +139,7 @@ public class DmiDataOperations {
final String requestId,
final String authorization) {
- final Set<String> cmHandlesIds
- = getDistinctCmHandleIds(dataOperationRequest);
+ final Set<String> cmHandlesIds = getDistinctCmHandleIds(dataOperationRequest);
final Collection<YangModelCmHandle> yangModelCmHandles
= inventoryPersistence.getYangModelCmHandles(cmHandlesIds);
@@ -170,7 +170,10 @@ public class DmiDataOperations {
final String requestData,
final String dataType,
final String authorization) {
- final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
+ final CmResourceAddress cmResourceAddress =
+ new CmResourceAddress(PASSTHROUGH_RUNNING.getDatastoreName(), cmHandleId, resourceId);
+
+ final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.getResolvedCmHandleId());
policyExecutor.checkPermission(yangModelCmHandle, operationType, authorization, resourceId, requestData);
@@ -212,7 +215,7 @@ public class DmiDataOperations {
final String optionsParamInQuery,
final String topicParamInQuery) {
final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(DATA);
- return DmiServiceUrlTemplateBuilder.newInstance()
+ return RestServiceUrlTemplateBuilder.newInstance()
.fixedPathSegment("ch")
.variablePathSegment("cmHandleId", yangModelCmHandle.getId())
.fixedPathSegment("data")
@@ -227,7 +230,7 @@ public class DmiDataOperations {
private UrlTemplateParameters getUrlTemplateParameters(final String dmiServiceName,
final String requestId,
final String topicParamInQuery) {
- return DmiServiceUrlTemplateBuilder.newInstance()
+ return RestServiceUrlTemplateBuilder.newInstance()
.fixedPathSegment("data")
.queryParameter("requestId", requestId)
.queryParameter("topic", topicParamInQuery)
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java
index bff2f6390c..01022cc03e 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java
@@ -61,9 +61,9 @@ public class NcmpCachedResourceRequestHandler extends NcmpDatastoreRequestHandle
final String authorization) {
final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendants);
- final DataNode dataNode = cpsDataService.getDataNodes(cmResourceAddress.datastoreName(),
- cmResourceAddress.cmHandleId(),
- cmResourceAddress.resourceIdentifier(),
+ final DataNode dataNode = cpsDataService.getDataNodes(cmResourceAddress.getDatastoreName(),
+ cmResourceAddress.getResolvedCmHandleId(),
+ cmResourceAddress.getResourceIdentifier(),
fetchDescendantsOption).iterator().next();
return Mono.justOrEmpty(dataNode);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java
index 503915716e..5343a2e131 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java
@@ -33,6 +33,7 @@ import org.onap.cps.ncmp.api.data.models.CmResourceAddress;
import org.onap.cps.ncmp.api.data.models.DataOperationRequest;
import org.onap.cps.ncmp.api.data.models.DatastoreType;
import org.onap.cps.ncmp.api.data.models.OperationType;
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher;
import org.onap.cps.spi.model.DataNode;
import org.springframework.stereotype.Service;
@@ -44,34 +45,35 @@ public class NetworkCmProxyFacade {
private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler;
private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler;
private final DmiDataOperations dmiDataOperations;
+ private final AlternateIdMatcher alternateIdMatcher;
/**
* Fetches resource data for a given data store using DMI (Data Management Interface).
* This method retrieves data based on the provided CmResourceAddress and additional query parameters.
* It supports asynchronous processing and handles authorization if required.
*
- * @param cmResourceAddress The target data store, including the CM handle and resource identifier.
- * This parameter must not be null.
- * @param options Additional query parameters that may influence the data retrieval process,
- * such as filters or limits. This parameter can be null.
- * @param topic The topic name for triggering asynchronous responses. If specified,
- * the response will be sent to this topic. This parameter can be null.
- * @param includeDescendants include (all) descendants or not
- * @param authorization The contents of the Authorization header. This parameter can be null
- * if authorization is not required.
+ * @param cmResourceAddress The target data store, including the CM handle and resource identifier.
+ * This parameter must not be null.
+ * @param optionsParamInQuery Additional query parameters that may influence the data retrieval process,
+ * such as filters or limits. This parameter can be null.
+ * @param topicParamInQuery The topic name for triggering asynchronous responses. If specified,
+ * the response will be sent to this topic. This parameter can be null.
+ * @param includeDescendants include (all) descendants or not
+ * @param authorization The contents of the Authorization header. This parameter can be null
+ * if authorization is not required.
* @return the result object, depends on use op topic. With topic a map object with request id is returned
* otherwise the result of the request.
*/
public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress,
- final String options,
- final String topic,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
final Boolean includeDescendants,
final String authorization) {
- final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler
- = getNcmpDatastoreRequestHandler(cmResourceAddress.datastoreName());
- return ncmpDatastoreRequestHandler.executeRequest(cmResourceAddress, options, topic, includeDescendants,
- authorization);
+ final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler
+ = getNcmpDatastoreRequestHandler(cmResourceAddress.getDatastoreName());
+ return ncmpDatastoreRequestHandler.executeRequest(cmResourceAddress, optionsParamInQuery,
+ topicParamInQuery, includeDescendants, authorization);
}
/**
@@ -99,7 +101,7 @@ public class NetworkCmProxyFacade {
/**
* Write resource data for data store pass-through running using dmi for given cm-handle.
*
- * @param cmHandleId cm handle identifier
+ * @param cmHandleReference cm handle or alternate identifier
* @param resourceIdentifier resource identifier
* @param operationType required operation type
* @param requestData request body to create resource
@@ -107,17 +109,16 @@ public class NetworkCmProxyFacade {
* @param authorization contents of Authorization header, or null if not present
* @return {@code Object} return data
*/
- public Object writeResourceDataPassThroughRunningForCmHandle(final String cmHandleId,
+ public Object writeResourceDataPassThroughRunningForCmHandle(final String cmHandleReference,
final String resourceIdentifier,
final OperationType operationType,
final String requestData,
final String dataType,
final String authorization) {
- return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier,
+ return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleReference, resourceIdentifier,
operationType, requestData, dataType, authorization);
}
-
private NcmpDatastoreRequestHandler getNcmpDatastoreRequestHandler(final String datastoreName) {
if (OPERATIONAL.equals(DatastoreType.fromDatastoreName(datastoreName))) {
return ncmpCachedResourceRequestHandler;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/PolicyExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/PolicyExecutor.java
deleted file mode 100644
index 2b5eb9e792..0000000000
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/PolicyExecutor.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2024 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.impl.data;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.data.models.OperationType;
-import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class PolicyExecutor {
-
- @Value("${ncmp.policy-executor.enabled:false}")
- private boolean enabled;
-
- @Value("${ncmp.policy-executor.server.address:http://policy-executor}")
- private String serverAddress;
-
- @Value("${ncmp.policy-executor.server.port:8080}")
- private String serverPort;
-
- private static final String PAYLOAD_TYPE_PREFIX = "cm_";
-
- /**
- * Use the Policy Executor to check permission for a cm write operation.
- * Wil throw an exception when the operation is not permitted (work in progress)
- *
- * @param yangModelCmHandle the cm handle involved
- * @param operationType the write operation
- * @param authorization the original rest authorization token (can be used to determine the client)
- * @param resourceIdentifier the resource identifier (can be blank)
- * @param changeRequestAsJson the change details from the original rest request in json format
- */
- public void checkPermission(final YangModelCmHandle yangModelCmHandle,
- final OperationType operationType,
- final String authorization,
- final String resourceIdentifier,
- final String changeRequestAsJson) {
- if (enabled) {
- final String payloadType = PAYLOAD_TYPE_PREFIX + operationType.getOperationName();
- log.info("Policy Executor Enabled");
- log.info("Address : {}", serverAddress);
- log.info("Port : {}", serverPort);
- log.info("Authorization : {}", authorization);
- log.info("Payload Type : {}", payloadType);
- log.info("Target FDN : {}", yangModelCmHandle.getAlternateId());
- log.info("CM Handle Id : {}", yangModelCmHandle.getId());
- log.info("Resource Identifier : {}", resourceIdentifier);
- log.info("Change Request (json) : {}", changeRequestAsJson);
- }
- }
-}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java
new file mode 100644
index 0000000000..af4331893d
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java
@@ -0,0 +1,225 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.data.policyexecutor;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.net.UnknownHostException;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.data.models.OperationType;
+import org.onap.cps.ncmp.api.exceptions.NcmpException;
+import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException;
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
+import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PolicyExecutor {
+
+ @Value("${ncmp.policy-executor.enabled:false}")
+ private boolean enabled;
+
+ @Value("${ncmp.policy-executor.defaultDecision:deny}")
+ private String defaultDecision;
+
+ @Value("${ncmp.policy-executor.server.address:http://policy-executor}")
+ private String serverAddress;
+
+ @Value("${ncmp.policy-executor.server.port:8080}")
+ private String serverPort;
+
+ @Value("${ncmp.policy-executor.httpclient.all-services.readTimeoutInSeconds:30}")
+ private long readTimeoutInSeconds;
+
+ @Qualifier("policyExecutorWebClient")
+ private final WebClient policyExecutorWebClient;
+
+ private final ObjectMapper objectMapper;
+
+ private static final Throwable NO_ERROR = null;
+
+ /**
+ * Use the Policy Executor to check permission for a cm write operation.
+ * Wil throw an exception when the operation is not permitted (work in progress)
+ *
+ * @param yangModelCmHandle the cm handle involved
+ * @param operationType the write operation
+ * @param authorization the original rest authorization token (can be used to determine the client)
+ * @param resourceIdentifier the resource identifier (can be blank)
+ * @param changeRequestAsJson the change details from the original rest request in json format
+ */
+ public void checkPermission(final YangModelCmHandle yangModelCmHandle,
+ final OperationType operationType,
+ final String authorization,
+ final String resourceIdentifier,
+ final String changeRequestAsJson) {
+ log.trace("Policy Executor Enabled: {}", enabled);
+ if (enabled) {
+ try {
+ final ResponseEntity<JsonNode> responseEntity = getPolicyExecutorResponse(yangModelCmHandle,
+ operationType,
+ authorization,
+ resourceIdentifier,
+ changeRequestAsJson);
+ final JsonNode responseBody = responseEntity.getBody();
+ if (responseBody == null) {
+ log.warn("No valid response body from Policy Executor, ignored");
+ return;
+ }
+ processSuccessResponse(responseBody);
+ } catch (final RuntimeException runtimeException) {
+ processException(runtimeException);
+ }
+ }
+ }
+
+ private Map<String, Object> getSingleRequestAsMap(final YangModelCmHandle yangModelCmHandle,
+ final OperationType operationType,
+ final String resourceIdentifier,
+ final String changeRequestAsJson) {
+ final Map<String, Object> data = new HashMap<>(4);
+ data.put("cmHandleId", yangModelCmHandle.getId());
+ data.put("resourceIdentifier", resourceIdentifier);
+ data.put("targetIdentifier", yangModelCmHandle.getAlternateId());
+ if (!OperationType.DELETE.equals(operationType)) {
+ try {
+ final Object changeRequestAsObject = objectMapper.readValue(changeRequestAsJson, Object.class);
+ data.put("cmChangeRequest", changeRequestAsObject);
+ } catch (final JsonProcessingException e) {
+ throw new NcmpException("Cannot convert Change Request data to Object",
+ "Invalid Json: " + changeRequestAsJson);
+ }
+ }
+ final Map<String, Object> request = new HashMap<>(2);
+ request.put("schema", getAssociatedPolicyDataSchemaName(operationType));
+ request.put("data", data);
+ return request;
+ }
+
+ private static String getAssociatedPolicyDataSchemaName(final OperationType operationType) {
+ return "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-" + operationType.getOperationName() + "-schema:1.0.0";
+ }
+
+ private Object createBodyAsObject(final List<Object> requests) {
+ final Map<String, Object> bodyAsMap = new HashMap<>(2);
+ bodyAsMap.put("decisionType", "allow");
+ bodyAsMap.put("requests", requests);
+ return bodyAsMap;
+ }
+
+ private ResponseEntity<JsonNode> getPolicyExecutorResponse(final YangModelCmHandle yangModelCmHandle,
+ final OperationType operationType,
+ final String authorization,
+ final String resourceIdentifier,
+ final String changeRequestAsJson) {
+ final Map<String, Object> requestAsMap = getSingleRequestAsMap(yangModelCmHandle,
+ operationType,
+ resourceIdentifier,
+ changeRequestAsJson);
+
+ final Object bodyAsObject = createBodyAsObject(Collections.singletonList(requestAsMap));
+
+ final UrlTemplateParameters urlTemplateParameters = RestServiceUrlTemplateBuilder.newInstance()
+ .fixedPathSegment("execute")
+ .createUrlTemplateParameters(String.format("%s:%s", serverAddress, serverPort),
+ "policy-executor/api");
+
+ return policyExecutorWebClient.post()
+ .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables())
+ .header(HttpHeaders.AUTHORIZATION, authorization)
+ .body(BodyInserters.fromValue(bodyAsObject))
+ .retrieve()
+ .toEntity(JsonNode.class)
+ .timeout(Duration.of(readTimeoutInSeconds, ChronoUnit.SECONDS))
+ .block();
+ }
+
+ private static void processSuccessResponse(final JsonNode responseBody) {
+ final String decisionId = responseBody.path("decisionId").asText("unknown id");
+ final String decision = responseBody.path("decision").asText("unknown");
+ final String messageFromPolicyExecutor = responseBody.path("message").asText();
+ processDecision(decisionId, decision, messageFromPolicyExecutor, NO_ERROR);
+ }
+
+ private static void processDecision(final String decisionId,
+ final String decision,
+ final String details,
+ final Throwable optionalCauseOfError) {
+ log.trace("Policy Executor decision id: {} ", decisionId);
+ if ("allow".equals(decision)) {
+ log.trace("Operation allowed.");
+ } else {
+ log.warn("Policy Executor decision: {}", decision);
+ log.warn("Policy Executor message: {}", details);
+ final String message = "Operation not allowed. Decision id " + decisionId + " : " + decision;
+ throw new PolicyExecutorException(message, details, optionalCauseOfError);
+ }
+ }
+
+ private void processException(final RuntimeException runtimeException) {
+ if (runtimeException instanceof WebClientResponseException) {
+ final WebClientResponseException webClientResponseException = (WebClientResponseException) runtimeException;
+ final int httpStatusCode = webClientResponseException.getStatusCode().value();
+ processFallbackResponse("Policy Executor returned HTTP Status code " + httpStatusCode + ".",
+ webClientResponseException);
+ } else {
+ final Throwable cause = runtimeException.getCause();
+ if (cause instanceof TimeoutException) {
+ processFallbackResponse("Policy Executor request timed out.", cause);
+ } else if (cause instanceof UnknownHostException) {
+ final String message
+ = String.format("Cannot connect to Policy Executor (%s:%s).", serverAddress, serverPort);
+ processFallbackResponse(message, cause);
+ } else {
+ log.warn("Request to Policy Executor failed with unexpected exception", runtimeException);
+ throw runtimeException;
+ }
+ }
+ }
+
+ private void processFallbackResponse(final String message, final Throwable cause) {
+ final String decisionId = "N/A";
+ final String decision = defaultDecision;
+ final String warning = message + " Falling back to configured default decision: " + defaultDecision;
+ log.warn(warning);
+ processDecision(decisionId, decision, warning, cause);
+ }
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImpl.java
new file mode 100644
index 0000000000..8934c088a1
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImpl.java
@@ -0,0 +1,55 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.datajobs;
+
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.datajobs.DataJobResultService;
+import org.onap.cps.ncmp.impl.dmi.DmiProperties;
+import org.onap.cps.ncmp.impl.dmi.DmiRestClient;
+import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DataJobResultServiceImpl implements DataJobResultService {
+
+ private final DmiRestClient dmiRestClient;
+ private final DmiProperties dmiProperties;
+
+ @Override
+ public String getDataJobResult(final String authorization,
+ final String dmiServiceName,
+ final String dataProducerId,
+ final String dataProducerJobId,
+ final String destination) {
+ final UrlTemplateParameters urlTemplateParameters = RestServiceUrlTemplateBuilder.newInstance()
+ .fixedPathSegment("cmwriteJob")
+ .fixedPathSegment("dataProducer")
+ .variablePathSegment("dataProducerId", dataProducerId)
+ .fixedPathSegment("dataProducerJob")
+ .variablePathSegment("dataProducerJobId", dataProducerJobId)
+ .fixedPathSegment("result")
+ .queryParameter("destination", destination)
+ .createUrlTemplateParameters(dmiServiceName, dmiProperties.getDmiBasePath());
+ return dmiRestClient.getDataJobResult(urlTemplateParameters, authorization).block();
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java
index 56ed6e30da..04c3ad2fc6 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java
@@ -42,19 +42,26 @@ public class DataJobServiceImpl implements DataJobService {
private final WriteRequestExaminer writeRequestExaminer;
@Override
- public void readDataJob(final String dataJobId, final DataJobMetadata dataJobMetadata,
+ public void readDataJob(final String authorization,
+ final String dataJobId,
+ final DataJobMetadata dataJobMetadata,
final DataJobReadRequest dataJobReadRequest) {
log.info("data job id for read operation is: {}", dataJobId);
}
@Override
- public List<SubJobWriteResponse> writeDataJob(final String dataJobId, final DataJobMetadata dataJobMetadata,
+ public List<SubJobWriteResponse> writeDataJob(final String authorization,
+ final String dataJobId,
+ final DataJobMetadata dataJobMetadata,
final DataJobWriteRequest dataJobWriteRequest) {
log.info("data job id for write operation is: {}", dataJobId);
final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey =
writeRequestExaminer.splitDmiWriteOperationsFromRequest(dataJobId, dataJobWriteRequest);
- return dmiSubJobClient.sendRequestsToDmi(dataJobId, dataJobMetadata, dmiWriteOperationsPerProducerKey);
+ return dmiSubJobClient.sendRequestsToDmi(authorization,
+ dataJobId,
+ dataJobMetadata,
+ dmiWriteOperationsPerProducerKey);
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImpl.java
new file mode 100644
index 0000000000..1cfb8a9dff
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImpl.java
@@ -0,0 +1,66 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.datajobs;
+
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.datajobs.DataJobStatusService;
+import org.onap.cps.ncmp.impl.dmi.DmiProperties;
+import org.onap.cps.ncmp.impl.dmi.DmiRestClient;
+import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
+import org.springframework.stereotype.Service;
+
+/**
+ * Implementation of {@link DataJobStatusService} interface.
+ * The operations interact with a DMI Plugin to retrieve data job statuses.
+ */
+@Service
+@RequiredArgsConstructor
+public class DataJobStatusServiceImpl implements DataJobStatusService {
+
+ private final DmiRestClient dmiRestClient;
+ private final DmiProperties dmiProperties;
+
+ @Override
+ public String getDataJobStatus(final String authorization,
+ final String dmiServiceName,
+ final String dataProducerId,
+ final String dataProducerJobId) {
+
+ final UrlTemplateParameters urlTemplateParameters = buildUrlParameters(dmiServiceName,
+ dataProducerId,
+ dataProducerJobId);
+ return dmiRestClient.getDataJobStatus(urlTemplateParameters, authorization).block();
+ }
+
+ private UrlTemplateParameters buildUrlParameters(final String dmiServiceName,
+ final String dataProducerId,
+ final String dataProducerJobId) {
+ return RestServiceUrlTemplateBuilder.newInstance()
+ .fixedPathSegment("cmwriteJob")
+ .fixedPathSegment("dataProducer")
+ .variablePathSegment("dataProducerId", dataProducerId)
+ .fixedPathSegment("dataProducerJob")
+ .variablePathSegment("dataProducerJobId", dataProducerJobId)
+ .fixedPathSegment("status")
+ .createUrlTemplateParameters(dmiServiceName, dmiProperties.getDmiBasePath());
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java
index 3d1c5237d6..a118d53e7e 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java
@@ -33,9 +33,9 @@ import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteRequest;
import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse;
import org.onap.cps.ncmp.impl.dmi.DmiProperties;
import org.onap.cps.ncmp.impl.dmi.DmiRestClient;
-import org.onap.cps.ncmp.impl.dmi.DmiServiceUrlTemplateBuilder;
-import org.onap.cps.ncmp.impl.dmi.UrlTemplateParameters;
import org.onap.cps.ncmp.impl.models.RequiredDmiService;
+import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
@@ -48,41 +48,49 @@ public class DmiSubJobRequestHandler {
private final DmiRestClient dmiRestClient;
private final DmiProperties dmiProperties;
private final JsonObjectMapper jsonObjectMapper;
- static final String NO_AUTH_HEADER = null;
/**
* Sends sub-job write requests to the DMI Plugin.
*
- * @param dataJobId data ojb identifier
+ * @param authorization the authorization header from the REST request
+ * @param dataJobId data job identifier
* @param dataJobMetadata the data job's metadata
- * @param dmiWriteOperationsPerProducerKey a collection of write requests per producer key.
+ * @param dmiWriteOperationsPerProducerKey a collection of write requests per producer key
* @return a list of sub-job write responses
*/
- public List<SubJobWriteResponse> sendRequestsToDmi(final String dataJobId, final DataJobMetadata dataJobMetadata,
+ public List<SubJobWriteResponse> sendRequestsToDmi(final String authorization,
+ final String dataJobId,
+ final DataJobMetadata dataJobMetadata,
final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey) {
final List<SubJobWriteResponse> subJobWriteResponses = new ArrayList<>(dmiWriteOperationsPerProducerKey.size());
dmiWriteOperationsPerProducerKey.forEach((producerKey, dmi3ggpWriteOperations) -> {
- final SubJobWriteRequest subJobWriteRequest = new SubJobWriteRequest(dataJobMetadata.dataAcceptType(),
- dataJobMetadata.dataContentType(), dataJobId, dmi3ggpWriteOperations);
+ final SubJobWriteRequest subJobWriteRequest = new SubJobWriteRequest(dataJobMetadata.destination(),
+ dataJobMetadata.dataAcceptType(),
+ dataJobMetadata.dataContentType(),
+ producerKey.dataProducerIdentifier(),
+ dataJobId,
+ dmi3ggpWriteOperations);
- final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(dataJobId, producerKey);
+ final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(dataJobMetadata.destination(),
+ producerKey);
final ResponseEntity<Object> responseEntity = dmiRestClient.synchronousPostOperationWithJsonData(
RequiredDmiService.DATA,
urlTemplateParameters,
jsonObjectMapper.asJsonString(subJobWriteRequest),
OperationType.CREATE,
- NO_AUTH_HEADER);
- final SubJobWriteResponse subJobWriteResponse = (SubJobWriteResponse) responseEntity.getBody();
+ authorization);
+ final SubJobWriteResponse subJobWriteResponse = jsonObjectMapper
+ .convertToValueType(responseEntity.getBody(), SubJobWriteResponse.class);
log.debug("Sub job write response: {}", subJobWriteResponse);
subJobWriteResponses.add(subJobWriteResponse);
});
return subJobWriteResponses;
}
- private UrlTemplateParameters getUrlTemplateParameters(final String dataJobId, final ProducerKey producerKey) {
- return DmiServiceUrlTemplateBuilder.newInstance()
- .fixedPathSegment("writeJob")
- .variablePathSegment("requestId", dataJobId)
+ private UrlTemplateParameters getUrlTemplateParameters(final String destination, final ProducerKey producerKey) {
+ return RestServiceUrlTemplateBuilder.newInstance()
+ .fixedPathSegment("cmwriteJob")
+ .queryParameter("destination", destination)
.createUrlTemplateParameters(producerKey.dmiServiceName(), dmiProperties.getDmiBasePath());
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiRestClient.java
index 7ac85cbf84..a177272dff 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiRestClient.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiRestClient.java
@@ -34,6 +34,7 @@ import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.data.models.OperationType;
import org.onap.cps.ncmp.api.exceptions.DmiClientRequestException;
import org.onap.cps.ncmp.impl.models.RequiredDmiService;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpHeaders;
@@ -144,6 +145,44 @@ public class DmiRestClient {
.defaultIfEmpty(NOT_SPECIFIED);
}
+ /**
+ * Retrieves the status of a data job from the DMI service.
+ *
+ * @param urlTemplateParameters The URL template parameters for the DMI data job status endpoint.
+ * @param authorization The authorization token to be added to the request headers.
+ * @return A Mono emitting the status of the data job as a String.
+ * @throws DmiClientRequestException If there is an error during the DMI request.
+ */
+ public Mono<String> getDataJobStatus(final UrlTemplateParameters urlTemplateParameters,
+ final String authorization) {
+
+ return dataServicesWebClient.get()
+ .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables())
+ .headers(httpHeaders -> configureHttpHeaders(httpHeaders, authorization))
+ .retrieve()
+ .bodyToMono(JsonNode.class)
+ .map(jsonNode -> jsonNode.path("status").asText())
+ .onErrorMap(throwable -> handleDmiClientException(throwable, OperationType.READ.getOperationName()));
+ }
+
+ /**
+ * Retrieves the result of a data job from the DMI service.
+ *
+ * @param urlTemplateParameters The URL template parameters for the DMI data job status endpoint.
+ * @param authorization The authorization token to be added to the request headers.
+ * @return A Mono emitting the result of the data job as a String.
+ * @throws DmiClientRequestException If there is an error during the DMI request.
+ */
+ public Mono<String> getDataJobResult(final UrlTemplateParameters urlTemplateParameters,
+ final String authorization) {
+ return dataServicesWebClient.get()
+ .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables())
+ .headers(httpHeaders -> configureHttpHeaders(httpHeaders, authorization))
+ .retrieve().bodyToMono(String.class)
+ .onErrorMap(throwable -> handleDmiClientException(throwable,
+ OperationType.READ.getOperationName()));
+ }
+
private WebClient getWebClient(final RequiredDmiService requiredDmiService) {
return requiredDmiService.equals(RequiredDmiService.DATA) ? dataServicesWebClient : modelServicesWebClient;
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfiguration.java
new file mode 100644
index 0000000000..4134a56ead
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfiguration.java
@@ -0,0 +1,68 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.dmi;
+
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.config.DmiHttpClientConfig;
+import org.onap.cps.ncmp.impl.utils.http.WebClientConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@Configuration
+@RequiredArgsConstructor
+public class DmiWebClientsConfiguration extends WebClientConfiguration {
+
+ private final DmiHttpClientConfig dmiHttpClientConfig;
+
+ /**
+ * Configures and creates a web client bean for DMI data services.
+ *
+ * @param webClientBuilder The builder instance to create the WebClient.
+ * @return a WebClient instance configured for data services.
+ */
+ @Bean
+ public WebClient dataServicesWebClient(final WebClient.Builder webClientBuilder) {
+ return configureWebClient(webClientBuilder, dmiHttpClientConfig.getDataServices());
+ }
+
+ /**
+ * Configures and creates a web client bean for DMI model services.
+ *
+ * @param webClientBuilder The builder instance to create the WebClient.
+ * @return a WebClient instance configured for model services.
+ */
+ @Bean
+ public WebClient modelServicesWebClient(final WebClient.Builder webClientBuilder) {
+ return configureWebClient(webClientBuilder, dmiHttpClientConfig.getModelServices());
+ }
+
+ /**
+ * Configures and creates a web client bean for DMI health check services.
+ *
+ * @param webClientBuilder The builder instance to create the WebClient.
+ * @return a WebClient instance configured for health check services.
+ */
+ @Bean
+ public WebClient healthChecksWebClient(final WebClient.Builder webClientBuilder) {
+ return configureWebClient(webClientBuilder, dmiHttpClientConfig.getHealthCheckServices());
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/AlternateIdChecker.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/AlternateIdChecker.java
index 1096980e1d..3600d6da32 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/AlternateIdChecker.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/AlternateIdChecker.java
@@ -22,13 +22,12 @@ package org.onap.cps.ncmp.impl.inventory;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
import java.util.Set;
+import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle;
-import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
import org.springframework.stereotype.Service;
@@ -46,59 +45,6 @@ public class AlternateIdChecker {
private static final String NO_CURRENT_ALTERNATE_ID = "";
/**
- * Check if the alternate can be applied to the given cm handle (id).
- * Conditions:
- * - proposed alternate is blank (it wil be ignored)
- * - proposed alternate is same as current (no change)
- * - proposed alternate is not in use for a different cm handle (in the DB)
- *
- * @param cmHandleId cm handle id
- * @param proposedAlternateId proposed alternate id
- * @return true if the new alternate id not in use or equal to current alternate id, false otherwise
- */
- public boolean canApplyAlternateId(final String cmHandleId, final String proposedAlternateId) {
- String currentAlternateId = "";
- try {
- final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
- currentAlternateId = yangModelCmHandle.getAlternateId();
- } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
- // work with blank current alternate id
- }
- return this.canApplyAlternateId(cmHandleId, currentAlternateId, proposedAlternateId);
- }
-
- /**
- * Check if the alternate can be applied to the given cm handle.
- * Conditions:
- * - proposed alternate is blank (it wil be ignored)
- * - proposed alternate is same as current (no change)
- * - proposed alternate is not in use for a different cm handle (in the DB)
- *
- * @param cmHandleId cm handle id
- * @param currentAlternateId current alternate id
- * @param proposedAlternateId new alternate id
- * @return true if the new alternate id not in use or equal to current alternate id, false otherwise
- */
- public boolean canApplyAlternateId(final String cmHandleId,
- final String currentAlternateId,
- final String proposedAlternateId) {
- if (StringUtils.isBlank(currentAlternateId)) {
- if (alternateIdAlreadyInDb(proposedAlternateId)) {
- log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already "
- + "assigned to a different cm handle", cmHandleId);
- return false;
- }
- return true;
- }
- if (currentAlternateId.equals(proposedAlternateId)) {
- return true;
- }
- log.warn("Alternate id update ignored, cannot update cm handle {}, already has an alternate id of {}",
- cmHandleId, currentAlternateId);
- return false;
- }
-
- /**
* Check all alternate ids of a batch of cm handles.
* Includes cross-checks in the batch itself for duplicates. Only the first entry encountered wil be accepted.
*
@@ -109,13 +55,13 @@ public class AlternateIdChecker {
public Collection<String> getIdsOfCmHandlesWithRejectedAlternateId(
final Collection<NcmpServiceCmHandle> newNcmpServiceCmHandles,
final Operation operation) {
- final Set<String> acceptedAlternateIds = new HashSet<>(newNcmpServiceCmHandles.size());
+ final Set<String> assignedAlternateIds = getAlternateIdsAlreadyInDb(newNcmpServiceCmHandles);
final Collection<String> rejectedCmHandleIds = new ArrayList<>();
for (final NcmpServiceCmHandle ncmpServiceCmHandle : newNcmpServiceCmHandles) {
final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
final String proposedAlternateId = ncmpServiceCmHandle.getAlternateId();
- if (isProposedAlternateIdAcceptable(proposedAlternateId, operation, acceptedAlternateIds, cmHandleId)) {
- acceptedAlternateIds.add(proposedAlternateId);
+ if (isProposedAlternateIdAcceptable(proposedAlternateId, operation, assignedAlternateIds, cmHandleId)) {
+ assignedAlternateIds.add(proposedAlternateId);
} else {
rejectedCmHandleIds.add(cmHandleId);
}
@@ -124,28 +70,46 @@ public class AlternateIdChecker {
}
private boolean isProposedAlternateIdAcceptable(final String proposedAlternateId, final Operation operation,
- final Set<String> acceptedAlternateIds, final String cmHandleId) {
- if (StringUtils.isEmpty(proposedAlternateId)) {
+ final Set<String> assignedAlternateIds, final String cmHandleId) {
+ if (StringUtils.isBlank(proposedAlternateId)) {
return true;
}
- if (acceptedAlternateIds.contains(proposedAlternateId)) {
- log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already "
- + "assigned to a different cm handle (in this batch)", cmHandleId);
+ final String currentAlternateId = getCurrentAlternateId(operation, cmHandleId);
+ if (currentAlternateId.equals(proposedAlternateId)) {
+ return true;
+ }
+ if (StringUtils.isNotBlank(currentAlternateId)) {
+ log.warn("Alternate id update ignored, cannot update cm handle {}, already has an alternate id of {}",
+ cmHandleId, currentAlternateId);
return false;
}
- if (Operation.CREATE.equals(operation)) {
- return canApplyAlternateId(cmHandleId, NO_CURRENT_ALTERNATE_ID, proposedAlternateId);
+ if (assignedAlternateIds.contains(proposedAlternateId)) {
+ log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already "
+ + "assigned to a different cm handle", cmHandleId);
+ return false;
}
- return canApplyAlternateId(cmHandleId, proposedAlternateId);
+ return true;
}
- private boolean alternateIdAlreadyInDb(final String alternateId) {
- try {
- inventoryPersistence.getCmHandleDataNodeByAlternateId(alternateId);
- } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
- return false;
+ private Set<String> getAlternateIdsAlreadyInDb(final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
+ final Set<String> alternateIdsToCheck = ncmpServiceCmHandles.stream()
+ .map(NcmpServiceCmHandle::getAlternateId)
+ .filter(StringUtils::isNotBlank)
+ .collect(Collectors.toSet());
+ return inventoryPersistence.getCmHandleDataNodesByAlternateIds(alternateIdsToCheck).stream()
+ .map(dataNode -> (String) dataNode.getLeaves().get("alternate-id"))
+ .collect(Collectors.toSet());
+ }
+
+ private String getCurrentAlternateId(final Operation operation, final String cmHandleId) {
+ if (operation == Operation.UPDATE) {
+ try {
+ return inventoryPersistence.getYangModelCmHandle(cmHandleId).getAlternateId();
+ } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
+ // work with blank current alternate id
+ }
}
- return true;
+ return NO_CURRENT_ALTERNATE_ID;
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java
index 795d7c9c4c..95c3c77b71 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@
package org.onap.cps.ncmp.impl.inventory;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState;
import org.onap.cps.spi.FetchDescendantsOption;
@@ -59,7 +58,7 @@ public interface CmHandleQueryService {
* @param cmHandleState cm handle state
* @return a list of data nodes representing the cm handles.
*/
- List<DataNode> queryCmHandlesByState(CmHandleState cmHandleState);
+ Collection<DataNode> queryCmHandlesByState(CmHandleState cmHandleState);
/**
* Method to return data nodes with ancestor representing the cm handles.
@@ -67,8 +66,7 @@ public interface CmHandleQueryService {
* @param cpsPath cps path for which the cmHandle is requested
* @return a list of data nodes representing the cm handles.
*/
- List<DataNode> queryCmHandleAncestorsByCpsPath(String cpsPath,
- FetchDescendantsOption fetchDescendantsOption);
+ Collection<DataNode> queryCmHandleAncestorsByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption);
/**
* Method to return data nodes representing the cm handles.
@@ -76,7 +74,7 @@ public interface CmHandleQueryService {
* @param cpsPath cps path for which the cmHandle is requested
* @return a list of data nodes representing the cm handles.
*/
- List<DataNode> queryNcmpRegistryByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption);
+ Collection<DataNode> queryNcmpRegistryByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption);
/**
* Method to check the state of a cm handle with given id.
@@ -93,7 +91,7 @@ public interface CmHandleQueryService {
* @param dataStoreSyncState sync state
* @return a list of data nodes representing the cm handles.
*/
- List<DataNode> queryCmHandlesByOperationalSyncState(DataStoreSyncState dataStoreSyncState);
+ Collection<DataNode> queryCmHandlesByOperationalSyncState(DataStoreSyncState dataStoreSyncState);
/**
* Get all cm handles ids by DMI plugin identifier.
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java
index e36477f8c8..71e7384208 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java
@@ -30,19 +30,20 @@ import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
+import org.onap.cps.api.CpsDataService;
+import org.onap.cps.api.CpsQueryService;
+import org.onap.cps.cpspath.parser.CpsPathUtil;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.ncmp.api.inventory.models.TrustLevel;
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState;
import org.onap.cps.ncmp.impl.inventory.models.ModelledDmiServiceLeaves;
import org.onap.cps.ncmp.impl.inventory.models.PropertyType;
import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelCacheConfig;
-import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.model.DataNode;
-import org.onap.cps.spi.utils.CpsValidator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@@ -52,7 +53,8 @@ public class CmHandleQueryServiceImpl implements CmHandleQueryService {
private static final String DESCENDANT_PATH = "//";
private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles";
- private final CpsDataPersistenceService cpsDataPersistenceService;
+ private final CpsDataService cpsDataService;
+ private final CpsQueryService cpsQueryService;
@Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN)
private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
@@ -81,21 +83,24 @@ public class CmHandleQueryServiceImpl implements CmHandleQueryService {
}
@Override
- public List<DataNode> queryCmHandlesByState(final CmHandleState cmHandleState) {
+ public Collection<DataNode> queryCmHandlesByState(final CmHandleState cmHandleState) {
return queryCmHandleAncestorsByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]",
INCLUDE_ALL_DESCENDANTS);
}
@Override
- public List<DataNode> queryNcmpRegistryByCpsPath(final String cpsPath,
+ public Collection<DataNode> queryNcmpRegistryByCpsPath(final String cpsPath,
final FetchDescendantsOption fetchDescendantsOption) {
- return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- cpsPath, fetchDescendantsOption);
+ return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cpsPath,
+ fetchDescendantsOption);
}
@Override
- public List<DataNode> queryCmHandleAncestorsByCpsPath(final String cpsPath,
+ public Collection<DataNode> queryCmHandleAncestorsByCpsPath(final String cpsPath,
final FetchDescendantsOption fetchDescendantsOption) {
+ if (CpsPathUtil.getCpsPathQuery(cpsPath).getXpathPrefix().endsWith("/cm-handles")) {
+ return queryNcmpRegistryByCpsPath(cpsPath, fetchDescendantsOption);
+ }
return queryNcmpRegistryByCpsPath(cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption);
}
@@ -107,7 +112,7 @@ public class CmHandleQueryServiceImpl implements CmHandleQueryService {
}
@Override
- public List<DataNode> queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) {
+ public Collection<DataNode> queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) {
return queryCmHandleAncestorsByCpsPath("//state/datastores" + "/operational[@sync-state=\""
+ dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS);
}
@@ -176,9 +181,9 @@ public class CmHandleQueryServiceImpl implements CmHandleQueryService {
return cmHandleIds;
}
- private List<DataNode> getCmHandlesByDmiPluginIdentifierAndDmiProperty(final String dmiPluginIdentifier,
- final String dmiProperty) {
- return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ private Collection<DataNode> getCmHandlesByDmiPluginIdentifierAndDmiProperty(final String dmiPluginIdentifier,
+ final String dmiProperty) {
+ return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@" + dmiProperty + "='" + dmiPluginIdentifier + "']",
OMIT_DESCENDANTS);
}
@@ -186,7 +191,7 @@ public class CmHandleQueryServiceImpl implements CmHandleQueryService {
private DataNode getCmHandleState(final String cmHandleId) {
cpsValidator.validateNameCharacters(cmHandleId);
final String xpath = NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']/state";
- return cpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ return cpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
xpath, OMIT_DESCENDANTS).iterator().next();
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationService.java
index d6ddd108ea..d9f7e38993 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationService.java
@@ -37,6 +37,7 @@ import com.hazelcast.map.IMap;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -58,13 +59,11 @@ import org.onap.cps.ncmp.impl.inventory.models.CmHandleState;
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
import org.onap.cps.ncmp.impl.inventory.sync.ModuleOperationsUtils;
import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventsCmHandleStateHandler;
-import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelCacheConfig;
import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager;
import org.onap.cps.spi.exceptions.AlreadyDefinedException;
import org.onap.cps.spi.exceptions.CpsException;
import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
import org.onap.cps.spi.exceptions.DataValidationException;
-import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Slf4j
@@ -79,11 +78,7 @@ public class CmHandleRegistrationService {
private final CpsDataService cpsDataService;
private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler;
private final IMap<String, Object> moduleSyncStartedOnCmHandles;
-
- @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN)
- private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
private final TrustLevelManager trustLevelManager;
-
private final AlternateIdChecker alternateIdChecker;
/**
@@ -98,7 +93,7 @@ public class CmHandleRegistrationService {
dmiPluginRegistration.validateDmiPluginRegistration();
final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse();
- setTrustLevelPerDmiPlugin(dmiPluginRegistration);
+ trustLevelManager.registerDmiPlugin(dmiPluginRegistration);
processRemovedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse);
@@ -153,7 +148,7 @@ public class CmHandleRegistrationService {
final Set<String> notDeletedCmHandles = new HashSet<>();
for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandleIds, DELETE_BATCH_SIZE)) {
try {
- batchDeleteCmHandlesFromDbAndModuleSyncMap(tobeRemovedCmHandleBatch);
+ batchDeleteCmHandlesFromDbAndCaches(tobeRemovedCmHandleBatch);
tobeRemovedCmHandleBatch.forEach(cmHandleId ->
cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)));
@@ -259,7 +254,7 @@ public class CmHandleRegistrationService {
ncmpServiceCmHandle.getRegistrationTrustLevel());
}
}
- trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId);
+ trustLevelManager.registerCmHandles(initialTrustLevelPerCmHandleId);
}
private static boolean moduleUpgradeCanBeSkipped(final YangModelCmHandle yangModelCmHandle,
@@ -280,7 +275,7 @@ public class CmHandleRegistrationService {
private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) {
try {
- deleteCmHandleFromDbAndModuleSyncMap(cmHandleId);
+ deleteCmHandleFromDbAndCaches(cmHandleId);
return CmHandleRegistrationResponse.createSuccessResponse(cmHandleId);
} catch (final DataNodeNotFoundException dataNodeNotFoundException) {
log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
@@ -303,15 +298,17 @@ public class CmHandleRegistrationService {
lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle);
}
- private void deleteCmHandleFromDbAndModuleSyncMap(final String cmHandleId) {
+ private void deleteCmHandleFromDbAndCaches(final String cmHandleId) {
inventoryPersistence.deleteSchemaSetWithCascade(cmHandleId);
inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']");
+ trustLevelManager.removeCmHandles(Collections.singleton(cmHandleId));
removeDeletedCmHandleFromModuleSyncMap(cmHandleId);
}
- private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection<String> cmHandleIds) {
+ private void batchDeleteCmHandlesFromDbAndCaches(final Collection<String> cmHandleIds) {
inventoryPersistence.deleteSchemaSetsWithCascade(cmHandleIds);
inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(cmHandleIds));
+ trustLevelManager.removeCmHandles(cmHandleIds);
cmHandleIds.forEach(this::removeDeletedCmHandleFromModuleSyncMap);
}
@@ -345,14 +342,6 @@ public class CmHandleRegistrationService {
return cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId).toList();
}
- private void setTrustLevelPerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) {
- if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) {
- trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiPlugin(), TrustLevel.COMPLETE);
- } else {
- trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiDataPlugin(), TrustLevel.COMPLETE);
- }
- }
-
private Collection<String> checkAlternateIds(
final List<NcmpServiceCmHandle> cmHandlesToBeCreated,
final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses) {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java
index c9981dba9a..308ead1270 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java
@@ -44,6 +44,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse;
import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle;
@@ -56,7 +57,6 @@ import org.onap.cps.spi.model.DataNodeBuilder;
import org.onap.cps.utils.ContentType;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.stereotype.Service;
-import org.springframework.util.StringUtils;
@Slf4j
@Service
@@ -112,8 +112,7 @@ public class CmHandleRegistrationServicePropertyHandler {
private void processUpdates(final DataNode existingCmHandleDataNode,
final NcmpServiceCmHandle updatedNcmpServiceCmHandle) {
- setAndUpdateCmHandleField(
- updatedNcmpServiceCmHandle.getCmHandleId(), "alternate-id", updatedNcmpServiceCmHandle.getAlternateId());
+ updateAlternateId(updatedNcmpServiceCmHandle);
updateDataProducerIdentifier(existingCmHandleDataNode, updatedNcmpServiceCmHandle);
if (!updatedNcmpServiceCmHandle.getPublicProperties().isEmpty()) {
updateProperties(existingCmHandleDataNode, PUBLIC_PROPERTY,
@@ -124,13 +123,20 @@ public class CmHandleRegistrationServicePropertyHandler {
}
}
+ private void updateAlternateId(final NcmpServiceCmHandle ncmpServiceCmHandle) {
+ final String newAlternateId = ncmpServiceCmHandle.getAlternateId();
+ if (StringUtils.isNotBlank(newAlternateId)) {
+ setAndUpdateCmHandleField(ncmpServiceCmHandle.getCmHandleId(), "alternate-id", newAlternateId);
+ }
+ }
+
private void updateDataProducerIdentifier(final DataNode cmHandleDataNode,
final NcmpServiceCmHandle ncmpServiceCmHandle) {
final String newDataProducerIdentifier = ncmpServiceCmHandle.getDataProducerIdentifier();
- if (StringUtils.hasText(newDataProducerIdentifier)) {
+ if (StringUtils.isNotBlank(newDataProducerIdentifier)) {
final YangModelCmHandle yangModelCmHandle = YangDataConverter.toYangModelCmHandle(cmHandleDataNode);
final String existingDataProducerIdentifier = yangModelCmHandle.getDataProducerIdentifier();
- if (StringUtils.hasText(existingDataProducerIdentifier)) {
+ if (StringUtils.isNotBlank(existingDataProducerIdentifier)) {
if (!existingDataProducerIdentifier.equals(newDataProducerIdentifier)) {
log.warn("Unable to update dataProducerIdentifier for cmHandle {}. "
+ "Value for dataProducerIdentifier has been set previously.",
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java
index cb4b04e4a0..a0d3a3eaee 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java
@@ -130,6 +130,14 @@ public interface InventoryPersistence extends NcmpPersistence {
DataNode getCmHandleDataNodeByAlternateId(String alternateId);
/**
+ * Get data nodes for the given batch of alternate ids.
+ *
+ * @param alternateIds alternate IDs
+ * @return data nodes
+ */
+ Collection<DataNode> getCmHandleDataNodesByAlternateIds(Collection<String> alternateIds);
+
+ /**
* Get collection of data nodes of given cm handles.
*
* @param cmHandleIds collection of cmHandle IDs
@@ -144,4 +152,12 @@ public interface InventoryPersistence extends NcmpPersistence {
* @return Collection of CM handle Ids
*/
Collection<String> getCmHandleIdsWithGivenModules(Collection<String> moduleNamesForQuery);
+
+ /**
+ * Check database if cm handle id exists if not return false.
+ *
+ * @param cmHandleId cmHandle Id
+ * @return Boolean
+ */
+ boolean isExistingCmHandleId(String cmHandleId);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
index 2930d4f87c..06c3f8d2f4 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2022-2024 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
- * Modifications Copyright (C) 2023 TechMahindra Ltd.
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,10 +32,12 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsModuleService;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.ncmp.api.inventory.models.CompositeState;
import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder;
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
@@ -46,7 +48,7 @@ import org.onap.cps.spi.exceptions.DataValidationException;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.ModuleDefinition;
import org.onap.cps.spi.model.ModuleReference;
-import org.onap.cps.spi.utils.CpsValidator;
+import org.onap.cps.utils.ContentType;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.stereotype.Component;
@@ -95,7 +97,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
public void saveCmHandleState(final String cmHandleId, final CompositeState compositeState) {
final String cmHandleJsonData = createStateJsonData(jsonObjectMapper.asJsonString(compositeState));
cpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- getXPathForCmHandleById(cmHandleId), cmHandleJsonData, OffsetDateTime.now());
+ getXPathForCmHandleById(cmHandleId), cmHandleJsonData, OffsetDateTime.now(), ContentType.JSON);
}
@Override
@@ -105,7 +107,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
getXPathForCmHandleById(cmHandleId),
createStateJsonData(jsonObjectMapper.asJsonString(compositeState))));
cpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- cmHandlesJsonDataMap, OffsetDateTime.now());
+ cmHandlesJsonDataMap, OffsetDateTime.now(), ContentType.JSON);
}
@Override
@@ -161,7 +163,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
Lists.partition(yangModelCmHandles, CMHANDLE_BATCH_SIZE)) {
final String cmHandlesJsonData = createCmHandlesJsonData(yangModelCmHandleBatch);
cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- NCMP_DMI_REGISTRY_PARENT, cmHandlesJsonData, NO_TIMESTAMP);
+ NCMP_DMI_REGISTRY_PARENT, cmHandlesJsonData, NO_TIMESTAMP, ContentType.JSON);
}
}
@@ -183,6 +185,15 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
}
@Override
+ public Collection<DataNode> getCmHandleDataNodesByAlternateIds(final Collection<String> alternateIds) {
+ if (alternateIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+ final String cpsPathForCmHandlesByAlternateIds = getCpsPathForCmHandlesByAlternateIds(alternateIds);
+ return cmHandleQueryService.queryNcmpRegistryByCpsPath(cpsPathForCmHandlesByAlternateIds, OMIT_DESCENDANTS);
+ }
+
+ @Override
public Collection<DataNode> getCmHandleDataNodes(final Collection<String> cmHandleIds) {
final Collection<String> xpaths = new ArrayList<>(cmHandleIds.size());
cmHandleIds.forEach(cmHandleId -> xpaths.add(getXPathForCmHandleById(cmHandleId)));
@@ -194,6 +205,15 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
return cpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery);
}
+ @Override
+ public boolean isExistingCmHandleId(final String cmHandleId) {
+ try {
+ return !getCmHandleDataNodeByCmHandleId(cmHandleId).isEmpty();
+ } catch (final DataNodeNotFoundException exception) {
+ return false;
+ }
+ }
+
private static String getXPathForCmHandleById(final String cmHandleId) {
return NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']";
}
@@ -202,6 +222,11 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
return NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@alternate-id='" + alternateId + "']";
}
+ private static String getCpsPathForCmHandlesByAlternateIds(final Collection<String> alternateIds) {
+ return alternateIds.stream().collect(Collectors.joining("' or @alternate-id='",
+ NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@alternate-id='", "']"));
+ }
+
private static String createStateJsonData(final String state) {
return "{\"state\":" + state + "}";
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java
index e44b6ba342..905b09ef74 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java
@@ -29,10 +29,10 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsModuleService;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
import org.onap.cps.spi.model.DataNode;
-import org.onap.cps.spi.utils.CpsValidator;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.stereotype.Component;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java
index b10155c4a9..76ee286635 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java
@@ -18,7 +18,6 @@
* ============LICENSE_END=========================================================
*/
-
package org.onap.cps.ncmp.impl.inventory.models;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -126,10 +125,9 @@ public class YangModelCmHandle {
yangModelCmHandle.setDmiServiceName(dmiServiceName);
yangModelCmHandle.setDmiDataServiceName(dmiDataServiceName);
yangModelCmHandle.setDmiModelServiceName(dmiModelServiceName);
- yangModelCmHandle.setModuleSetTag(moduleSetTag == null ? StringUtils.EMPTY : moduleSetTag);
- yangModelCmHandle.setAlternateId(alternateId == null ? StringUtils.EMPTY : alternateId);
- yangModelCmHandle.setDataProducerIdentifier(
- dataProducerIdentifier == null ? StringUtils.EMPTY : dataProducerIdentifier);
+ yangModelCmHandle.setModuleSetTag(StringUtils.trimToEmpty(moduleSetTag));
+ yangModelCmHandle.setAlternateId(StringUtils.trimToEmpty(alternateId));
+ yangModelCmHandle.setDataProducerIdentifier(StringUtils.trimToEmpty(dataProducerIdentifier));
yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties()));
yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties(
ncmpServiceCmHandle.getPublicProperties()));
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DataSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DataSyncWatchdog.java
index 45f6367841..c3973236f0 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DataSyncWatchdog.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DataSyncWatchdog.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,21 +60,22 @@ public class DataSyncWatchdog {
moduleOperationsUtils.getUnsynchronizedReadyCmHandles().forEach(unSynchronizedReadyCmHandle -> {
final String cmHandleId = unSynchronizedReadyCmHandle.getId();
if (hasPushedIntoSemaphoreMap(cmHandleId)) {
- log.debug("Executing data sync on {}", cmHandleId);
+ log.info("Executing data sync on {}", cmHandleId);
final CompositeState compositeState = inventoryPersistence
.getCmHandleState(cmHandleId);
final String resourceData = moduleOperationsUtils.getResourceData(cmHandleId);
if (resourceData == null) {
- log.debug("Error retrieving resource data for Cm-Handle: {}", cmHandleId);
+ log.error("Error retrieving resource data for Cm-Handle: {}", cmHandleId);
} else {
cpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId,
resourceData, OffsetDateTime.now());
setSyncStateToSynchronized().accept(compositeState);
inventoryPersistence.saveCmHandleState(cmHandleId, compositeState);
updateDataSyncSemaphoreMap(cmHandleId);
+ log.info("Data sync finished for {}", cmHandleId);
}
} else {
- log.debug("{} already processed by another instance", cmHandleId);
+ log.info("{} already processed by another instance", cmHandleId);
}
});
log.debug("No Cm-Handles currently found in READY State and Operational Sync State is UNSYNCHRONIZED");
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java
index 433c67f100..8ba70b3a31 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java
@@ -36,10 +36,10 @@ import lombok.RequiredArgsConstructor;
import org.onap.cps.ncmp.api.inventory.models.YangResource;
import org.onap.cps.ncmp.impl.dmi.DmiProperties;
import org.onap.cps.ncmp.impl.dmi.DmiRestClient;
-import org.onap.cps.ncmp.impl.dmi.DmiServiceUrlTemplateBuilder;
-import org.onap.cps.ncmp.impl.dmi.UrlTemplateParameters;
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
import org.onap.cps.ncmp.impl.models.DmiRequestBody;
+import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
import org.onap.cps.spi.model.ModuleReference;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.http.ResponseEntity;
@@ -107,7 +107,7 @@ public class DmiModelOperations {
final String jsonRequestBody,
final String cmHandle,
final String resourceName) {
- final UrlTemplateParameters urlTemplateParameters = DmiServiceUrlTemplateBuilder.newInstance()
+ final UrlTemplateParameters urlTemplateParameters = RestServiceUrlTemplateBuilder.newInstance()
.fixedPathSegment("ch")
.variablePathSegment("cmHandleId", cmHandle)
.fixedPathSegment(resourceName)
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java
index 69fb7f59f5..aae1769968 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java
@@ -22,10 +22,8 @@
package org.onap.cps.ncmp.impl.inventory.sync;
import com.fasterxml.jackson.databind.JsonNode;
-import java.time.Duration;
-import java.time.OffsetDateTime;
-import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -61,8 +59,7 @@ public class ModuleOperationsUtils {
private static final String RETRY_ATTEMPT_KEY = "attempt";
private static final String MODULE_SET_TAG_KEY = "moduleSetTag";
public static final String MODULE_SET_TAG_MESSAGE_FORMAT = "Upgrade to ModuleSetTag: %s";
- private static final String LOCK_REASON_DETAILS_MSG_FORMAT =
- MODULE_SET_TAG_MESSAGE_FORMAT + " Attempt #%d failed: %s";
+ private static final String LOCK_REASON_DETAILS_MSG_FORMAT = " Attempt #%d failed: %s";
private static final Pattern retryAttemptPattern = Pattern.compile("Attempt #(\\d+) failed:.+");
private static final Pattern moduleSetTagPattern = Pattern.compile("Upgrade to ModuleSetTag: (\\S+)");
private static final String CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE = """
@@ -75,8 +72,8 @@ public class ModuleOperationsUtils {
*
* @return cm handles (data nodes) in ADVISED state (empty list if none found)
*/
- public List<DataNode> getAdvisedCmHandles() {
- final List<DataNode> advisedCmHandlesAsDataNodes =
+ public Collection<DataNode> getAdvisedCmHandles() {
+ final Collection<DataNode> advisedCmHandlesAsDataNodes =
cmHandleQueryService.queryCmHandlesByState(CmHandleState.ADVISED);
log.debug("Total number of fetched advised cm handle(s) is (are) {}", advisedCmHandlesAsDataNodes.size());
return advisedCmHandlesAsDataNodes;
@@ -90,7 +87,7 @@ public class ModuleOperationsUtils {
* return empty list if not found
*/
public List<YangModelCmHandle> getUnsynchronizedReadyCmHandles() {
- final List<DataNode> unsynchronizedCmHandles = cmHandleQueryService
+ final Collection<DataNode> unsynchronizedCmHandles = cmHandleQueryService
.queryCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED);
final List<YangModelCmHandle> yangModelCmHandles = new ArrayList<>();
@@ -110,31 +107,33 @@ public class ModuleOperationsUtils {
*
* @return a random LOCKED yang model cm handle, return null if not found
*/
- public List<YangModelCmHandle> getCmHandlesThatFailedModelSyncOrUpgrade() {
- final List<DataNode> lockedCmHandlesAsDataNodeList
+ public Collection<YangModelCmHandle> getCmHandlesThatFailedModelSyncOrUpgrade() {
+ final Collection<DataNode> lockedCmHandlesAsDataNodeList
= cmHandleQueryService.queryCmHandleAncestorsByCpsPath(CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE,
FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
return convertCmHandlesDataNodesToYangModelCmHandles(lockedCmHandlesAsDataNodeList);
}
/**
- * Update Composite State attempts counter and set new lock reason and details.
+ * Updates the lock reason message and attempt counter for the provided CompositeState.
+ * This method increments the attempt counter and updates the lock reason message,
+ * including the module set tag if available.
*
- * @param lockReasonCategory lock reason category
- * @param errorMessage error message
+ * @param compositeState the composite state of the CM handle
+ * @param lockReasonCategory the lock reason category for the CM handle
+ * @param errorMessage the error message to include in the lock reason message
*/
- public void updateLockReasonDetailsAndAttempts(final CompositeState compositeState,
- final LockReasonCategory lockReasonCategory,
- final String errorMessage) {
- int attempt = 1;
- final Map<String, String> compositeStateDetails
- = getLockedCompositeStateDetails(compositeState.getLockReason());
- if (!compositeStateDetails.isEmpty() && compositeStateDetails.containsKey(RETRY_ATTEMPT_KEY)) {
- attempt = 1 + Integer.parseInt(compositeStateDetails.get(RETRY_ATTEMPT_KEY));
- }
- final String moduleSetTag = compositeStateDetails.getOrDefault(MODULE_SET_TAG_KEY, "");
+ public void updateLockReasonWithAttempts(final CompositeState compositeState,
+ final LockReasonCategory lockReasonCategory,
+ final String errorMessage) {
+ final Map<String, String> lockedStateDetails = getLockedCompositeStateDetails(compositeState.getLockReason());
+ final int nextAttemptCount = calculateNextAttemptCount(lockedStateDetails);
+ final String moduleSetTag = lockedStateDetails.getOrDefault(MODULE_SET_TAG_KEY, "");
+
+ final String lockReasonMessage = buildLockReasonDetails(moduleSetTag, nextAttemptCount, errorMessage);
+
compositeState.setLockReason(CompositeState.LockReason.builder()
- .details(String.format(LOCK_REASON_DETAILS_MSG_FORMAT, moduleSetTag, attempt, errorMessage))
+ .details(lockReasonMessage)
.lockReasonCategory(lockReasonCategory)
.build());
}
@@ -165,37 +164,6 @@ public class ModuleOperationsUtils {
return Collections.emptyMap();
}
-
- /**
- * Check if a module sync retry is needed.
- *
- * @param compositeState the composite state currently in the locked state
- * @return if the retry mechanism should be attempted
- */
- public boolean needsModuleSyncRetryOrUpgrade(final CompositeState compositeState) {
- final OffsetDateTime time = OffsetDateTime.parse(compositeState.getLastUpdateTime(),
- DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
- final CompositeState.LockReason lockReason = compositeState.getLockReason();
-
- final boolean moduleUpgrade = LockReasonCategory.MODULE_UPGRADE == lockReason.getLockReasonCategory();
- if (moduleUpgrade) {
- log.info("Locked for module upgrade");
- return true;
- }
-
- final boolean failedDuringModuleSync = LockReasonCategory.MODULE_SYNC_FAILED
- == lockReason.getLockReasonCategory();
- final boolean failedDuringModuleUpgrade = LockReasonCategory.MODULE_UPGRADE_FAILED
- == lockReason.getLockReasonCategory();
-
- if (failedDuringModuleSync || failedDuringModuleUpgrade) {
- log.info("Locked for module {} (last attempt failed).", failedDuringModuleSync ? "sync" : "upgrade");
- return isRetryDue(lockReason, time);
- }
- log.info("Locked for other reason");
- return false;
- }
-
/**
* Get the Resource Data from Node through DMI Passthrough service.
*
@@ -236,27 +204,23 @@ public class ModuleOperationsUtils {
return jsonObjectMapper.asJsonString(Map.of(firstElement.getKey(), firstElement.getValue()));
}
- private List<YangModelCmHandle> convertCmHandlesDataNodesToYangModelCmHandles(
- final List<DataNode> cmHandlesAsDataNodeList) {
+ private Collection<YangModelCmHandle> convertCmHandlesDataNodesToYangModelCmHandles(
+ final Collection<DataNode> cmHandlesAsDataNodeList) {
return cmHandlesAsDataNodeList.stream().map(YangDataConverter::toYangModelCmHandle).toList();
}
- private boolean isRetryDue(final CompositeState.LockReason compositeStateLockReason, final OffsetDateTime time) {
- final int timeInMinutesUntilNextAttempt;
- final Map<String, String> compositeStateDetails = getLockedCompositeStateDetails(compositeStateLockReason);
- if (compositeStateDetails.isEmpty()) {
- timeInMinutesUntilNextAttempt = 1;
- log.info("First Attempt: no current attempts found.");
- } else {
- timeInMinutesUntilNextAttempt = (int) Math.pow(2, Integer.parseInt(compositeStateDetails
- .get(RETRY_ATTEMPT_KEY)));
- }
- final int timeSinceLastAttempt = (int) Duration.between(time, OffsetDateTime.now()).toMinutes();
- if (timeInMinutesUntilNextAttempt >= timeSinceLastAttempt) {
- log.info("Time until next attempt is {} minutes: ", timeInMinutesUntilNextAttempt - timeSinceLastAttempt);
- return false;
+ private int calculateNextAttemptCount(final Map<String, String> compositeStateDetails) {
+ return compositeStateDetails.containsKey(RETRY_ATTEMPT_KEY)
+ ? 1 + Integer.parseInt(compositeStateDetails.get(RETRY_ATTEMPT_KEY))
+ : 1;
+ }
+
+ private String buildLockReasonDetails(final String moduleSetTag, final int attempt, final String errorMessage) {
+ if (moduleSetTag.isEmpty()) {
+ return String.format(LOCK_REASON_DETAILS_MSG_FORMAT, attempt, errorMessage);
}
- log.info("Retry due now");
- return true;
+ return String.format(MODULE_SET_TAG_MESSAGE_FORMAT + " " + LOCK_REASON_DETAILS_MSG_FORMAT,
+ moduleSetTag, attempt, errorMessage);
}
+
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java
index d2bc3ada86..ca0f1c6a6d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java
@@ -29,23 +29,17 @@ import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NFP_OPERATIONAL_D
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsModuleService;
-import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService;
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState;
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
-import org.onap.cps.ncmp.impl.utils.YangDataConverter;
import org.onap.cps.spi.CascadeDeleteAllowed;
-import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
-import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.ModuleReference;
import org.onap.cps.utils.ContentType;
import org.onap.cps.utils.JsonObjectMapper;
@@ -58,7 +52,6 @@ public class ModuleSyncService {
private final DmiModelOperations dmiModelOperations;
private final CpsModuleService cpsModuleService;
- private final CmHandleQueryService cmHandleQueryService;
private final CpsDataService cpsDataService;
private final CpsAnchorService cpsAnchorService;
private final JsonObjectMapper jsonObjectMapper;
@@ -113,34 +106,25 @@ public class ModuleSyncService {
}
private ModuleDelta getModuleDelta(final YangModelCmHandle yangModelCmHandle, final String targetModuleSetTag) {
- final Collection<ModuleReference> allModuleReferences;
final Map<String, String> newYangResources;
-
- final YangModelCmHandle cmHandleWithSameModuleSetTag = getAnyReadyCmHandleByModuleSetTag(targetModuleSetTag);
- if (cmHandleWithSameModuleSetTag == null) {
+ Collection<ModuleReference> allModuleReferences = getModuleReferencesByModuleSetTag(targetModuleSetTag);
+ if (allModuleReferences.isEmpty()) {
allModuleReferences = dmiModelOperations.getModuleReferences(yangModelCmHandle);
newYangResources = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
cpsModuleService.identifyNewModuleReferences(allModuleReferences));
} else {
log.info("Found other cm handle having same module set tag: {}", targetModuleSetTag);
- allModuleReferences = cpsModuleService.getYangResourcesModuleReferences(
- NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleWithSameModuleSetTag.getId());
newYangResources = NO_NEW_MODULES;
}
return new ModuleDelta(allModuleReferences, newYangResources);
}
- private YangModelCmHandle getAnyReadyCmHandleByModuleSetTag(final String moduleSetTag) {
- if (StringUtils.isBlank(moduleSetTag)) {
- return null;
+ private Collection<ModuleReference> getModuleReferencesByModuleSetTag(final String moduleSetTag) {
+ if (moduleSetTag == null || moduleSetTag.trim().isEmpty()) {
+ return Collections.emptyList();
}
- final String escapedModuleSetTag = moduleSetTag.replace("'", "''");
- final List<DataNode> dataNodes = cmHandleQueryService.queryNcmpRegistryByCpsPath(
- NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@module-set-tag='" + escapedModuleSetTag + "']",
- FetchDescendantsOption.DIRECT_CHILDREN_ONLY);
- return dataNodes.stream().map(YangDataConverter::toYangModelCmHandle)
- .filter(cmHandle -> cmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY)
- .findFirst().orElse(null);
+ return cpsModuleService.getModuleReferencesByAttribute(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ Map.of("module-set-tag", moduleSetTag), Map.of("cm-handle-state", CmHandleState.READY.name()));
}
private void setCmHandleModuleSetTag(final YangModelCmHandle yangModelCmHandle, final String newModuleSetTag) {
@@ -149,4 +133,5 @@ public class ModuleSyncService {
cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
jsonForUpdate, OffsetDateTime.now(), ContentType.JSON);
}
+
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java
index 39ea38aafc..e627f8f894 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java
@@ -23,7 +23,6 @@ package org.onap.cps.ncmp.impl.inventory.sync;
import com.hazelcast.map.IMap;
import java.util.Collection;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
@@ -80,7 +79,7 @@ public class ModuleSyncTasks {
log.warn("Processing of {} module failed due to reason {}.", cmHandleId, e.getMessage());
final LockReasonCategory lockReasonCategory = inUpgrade ? LockReasonCategory.MODULE_UPGRADE_FAILED
: LockReasonCategory.MODULE_SYNC_FAILED;
- moduleOperationsUtils.updateLockReasonDetailsAndAttempts(compositeState,
+ moduleOperationsUtils.updateLockReasonWithAttempts(compositeState,
lockReasonCategory, e.getMessage());
setCmHandleStateLocked(yangModelCmHandle, compositeState.getLockReason());
cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED);
@@ -96,23 +95,22 @@ public class ModuleSyncTasks {
}
/**
- * Reset state to "ADVISED" for any previously failed cm handles.
+ * Set the state of CM handles to ADVISED.
+ * This method processes a collection of CM handles, logs their lock reason, and resets their state
+ * to ADVISED. Once reset, it updates the CM handle states in a batch to allow for re-attempt by the module-sync
+ * watchdog.
*
- * @param failedCmHandles previously failed (locked) cm handles
+ * @param yangModelCmHandles a collection of CM handles that needs their state reset
*/
- public void resetFailedCmHandles(final List<YangModelCmHandle> failedCmHandles) {
- final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle = new HashMap<>(failedCmHandles.size());
- for (final YangModelCmHandle failedCmHandle : failedCmHandles) {
- final CompositeState compositeState = failedCmHandle.getCompositeState();
- final boolean isReadyForRetry = moduleOperationsUtils.needsModuleSyncRetryOrUpgrade(compositeState);
- log.info("Retry for cmHandleId : {} is {}", failedCmHandle.getId(), isReadyForRetry);
- if (isReadyForRetry) {
- final String resetCmHandleId = failedCmHandle.getId();
- log.debug("Reset cm handle {} state to ADVISED to be re-attempted by module-sync watchdog",
- resetCmHandleId);
- cmHandleStatePerCmHandle.put(failedCmHandle, CmHandleState.ADVISED);
- removeResetCmHandleFromModuleSyncMap(resetCmHandleId);
- }
+ public void setCmHandlesToAdvised(final Collection<YangModelCmHandle> yangModelCmHandles) {
+ final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle = new HashMap<>(yangModelCmHandles.size());
+ for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) {
+ final CompositeState compositeState = yangModelCmHandle.getCompositeState();
+ final String resetCmHandleId = yangModelCmHandle.getId();
+ log.debug("Resetting CM handle {} state to ADVISED for retry by the module-sync watchdog. Lock reason: {}",
+ yangModelCmHandle.getId(), compositeState.getLockReason().getLockReasonCategory().name());
+ cmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.ADVISED);
+ removeResetCmHandleFromModuleSyncMap(resetCmHandleId);
}
lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java
index b4dde18da0..4061298cd0 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-2024 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,10 +24,10 @@ package org.onap.cps.ncmp.impl.inventory.sync;
import com.hazelcast.map.IMap;
import java.util.Collection;
import java.util.HashSet;
-import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -46,6 +46,8 @@ public class ModuleSyncWatchdog {
private final IMap<String, Object> moduleSyncStartedOnCmHandles;
private final ModuleSyncTasks moduleSyncTasks;
private final AsyncTaskExecutor asyncTaskExecutor;
+ private final Lock workQueueLock;
+
private static final int MODULE_SYNC_BATCH_SIZE = 100;
private static final long PREVENT_CPU_BURN_WAIT_TIME_MILLIS = 10;
private static final String VALUE_FOR_HAZELCAST_IN_PROGRESS_MAP = "Started";
@@ -61,7 +63,7 @@ public class ModuleSyncWatchdog {
*/
@Scheduled(fixedDelayString = "${ncmp.timers.advised-modules-sync.sleep-time-ms:5000}")
public void moduleSyncAdvisedCmHandles() {
- log.info("Processing module sync watchdog waking up.");
+ log.debug("Processing module sync watchdog waking up.");
populateWorkQueueIfNeeded();
while (!moduleSyncWorkQueue.isEmpty()) {
if (batchCounter.get() <= asyncTaskExecutor.getAsyncTaskParallelismLevel()) {
@@ -80,21 +82,9 @@ public class ModuleSyncWatchdog {
}
}
- /**
- * Find any failed (locked) cm handles and change state back to 'ADVISED'.
- */
- @Scheduled(fixedDelayString = "${ncmp.timers.locked-modules-sync.sleep-time-ms:300000}")
- public void resetPreviouslyFailedCmHandles() {
- log.info("Processing module sync retry-watchdog waking up.");
- final List<YangModelCmHandle> failedCmHandles
- = moduleOperationsUtils.getCmHandlesThatFailedModelSyncOrUpgrade();
- log.info("Retrying {} cmHandles", failedCmHandles.size());
- moduleSyncTasks.resetFailedCmHandles(failedCmHandles);
- }
-
private void preventBusyWait() {
try {
- log.info("Busy waiting now");
+ log.debug("Busy waiting now");
TimeUnit.MILLISECONDS.sleep(PREVENT_CPU_BURN_WAIT_TIME_MILLIS);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
@@ -103,14 +93,46 @@ public class ModuleSyncWatchdog {
private void populateWorkQueueIfNeeded() {
if (moduleSyncWorkQueue.isEmpty()) {
- final List<DataNode> advisedCmHandles = moduleOperationsUtils.getAdvisedCmHandles();
- log.info("Processing module sync fetched {} advised cm handles from DB", advisedCmHandles.size());
- for (final DataNode advisedCmHandle : advisedCmHandles) {
- if (!moduleSyncWorkQueue.offer(advisedCmHandle)) {
- log.warn("Unable to add cm handle {} to the work queue", advisedCmHandle.getLeaves().get("id"));
+ if (workQueueLock.tryLock()) {
+ try {
+ populateWorkQueue();
+ if (moduleSyncWorkQueue.isEmpty()) {
+ setPreviouslyLockedCmHandlesToAdvised();
+ }
+ } finally {
+ workQueueLock.unlock();
}
}
- log.info("Work Queue Size : {}", moduleSyncWorkQueue.size());
+ }
+ }
+
+ private void populateWorkQueue() {
+ final Collection<DataNode> advisedCmHandles = moduleOperationsUtils.getAdvisedCmHandles();
+ if (advisedCmHandles.isEmpty()) {
+ log.debug("No advised CM handles found in DB.");
+ } else {
+ log.info("Fetched {} advised CM handles from DB. Adding them to the work queue.", advisedCmHandles.size());
+ advisedCmHandles.forEach(advisedCmHandle -> {
+ final String cmHandleId = String.valueOf(advisedCmHandle.getLeaves().get("id"));
+ if (moduleSyncWorkQueue.offer(advisedCmHandle)) {
+ log.info("CM handle {} added to the work queue.", cmHandleId);
+ } else {
+ log.warn("Failed to add CM handle {} to the work queue.", cmHandleId);
+ }
+ });
+ log.info("Work queue contains {} items.", moduleSyncWorkQueue.size());
+ }
+ }
+
+ private void setPreviouslyLockedCmHandlesToAdvised() {
+ final Collection<YangModelCmHandle> lockedCmHandles
+ = moduleOperationsUtils.getCmHandlesThatFailedModelSyncOrUpgrade();
+ if (lockedCmHandles.isEmpty()) {
+ log.debug("No locked CM handles found in DB.");
+ } else {
+ log.info("Found {} Locked CM Handles. Changing state to Advise to retry syncing them again.",
+ lockedCmHandles.size());
+ moduleSyncTasks.setCmHandlesToAdvised(lockedCmHandles);
}
}
@@ -131,8 +153,7 @@ public class ModuleSyncWatchdog {
nextBatch.add(batchCandidate);
}
}
- log.debug("nextBatch size : {}", nextBatch.size());
+ log.info("nextBatch size : {}", nextBatch.size());
return nextBatch;
}
-
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java
index 76b33cc8d9..1f33cc349d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java
@@ -24,8 +24,9 @@ import com.hazelcast.config.MapConfig;
import com.hazelcast.config.QueueConfig;
import com.hazelcast.map.IMap;
import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.locks.Lock;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.cache.HazelcastCacheConfig;
+import org.onap.cps.ncmp.impl.cache.HazelcastCacheConfig;
import org.onap.cps.spi.model.DataNode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -43,6 +44,7 @@ public class SynchronizationCacheConfig extends HazelcastCacheConfig {
private static final QueueConfig commonQueueConfig = createQueueConfig("defaultQueueConfig");
private static final MapConfig moduleSyncStartedConfig = createMapConfig("moduleSyncStartedConfig");
private static final MapConfig dataSyncSemaphoresConfig = createMapConfig("dataSyncSemaphoresConfig");
+ private static final String LOCK_NAME_FOR_WORK_QUEUE = "workQueueLock";
/**
* Module Sync Distributed Queue Instance.
@@ -51,7 +53,7 @@ public class SynchronizationCacheConfig extends HazelcastCacheConfig {
*/
@Bean
public BlockingQueue<DataNode> moduleSyncWorkQueue() {
- return createHazelcastInstance("moduleSyncWorkQueue", commonQueueConfig).getQueue("moduleSyncWorkQueue");
+ return getOrCreateHazelcastInstance(commonQueueConfig).getQueue("moduleSyncWorkQueue");
}
/**
@@ -61,7 +63,7 @@ public class SynchronizationCacheConfig extends HazelcastCacheConfig {
*/
@Bean
public IMap<String, Object> moduleSyncStartedOnCmHandles() {
- return createHazelcastInstance("moduleSyncStartedOnCmHandles", moduleSyncStartedConfig).getMap(
+ return getOrCreateHazelcastInstance(moduleSyncStartedConfig).getMap(
"moduleSyncStartedOnCmHandles");
}
@@ -72,6 +74,23 @@ public class SynchronizationCacheConfig extends HazelcastCacheConfig {
*/
@Bean
public IMap<String, Boolean> dataSyncSemaphores() {
- return createHazelcastInstance("dataSyncSemaphores", dataSyncSemaphoresConfig).getMap("dataSyncSemaphores");
+ return getOrCreateHazelcastInstance(dataSyncSemaphoresConfig).getMap("dataSyncSemaphores");
+ }
+
+ /**
+ * Retrieves a distributed lock used to control access to the work queue for module synchronization.
+ * This lock ensures that the population and modification of the work queue are thread-safe and
+ * protected from concurrent access across different nodes in the distributed system.
+ * The lock guarantees that only one instance of the application can populate or modify the
+ * module sync work queue at a time, preventing race conditions and potential data inconsistencies.
+ * The lock is obtained using the Hazelcast CP Subsystem's {@link Lock}, which provides
+ * strong consistency guarantees for distributed operations.
+ *
+ * @return a {@link Lock} instance used for synchronizing access to the work queue.
+ */
+ @Bean
+ public Lock workQueueLock() {
+ // TODO Method below does not use commonQueueConfig for creating lock (Refactor later)
+ return getOrCreateHazelcastInstance(commonQueueConfig).getCPSubsystem().getLock(LOCK_NAME_FOR_WORK_QUEUE);
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventType.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventType.java
index 4bc2f10218..1d4e3c8363 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventType.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventType.java
@@ -26,10 +26,10 @@ public enum LcmEventType {
private final String eventName;
- private final String eventTypeTemplate = "org.onap.ncmp.cmhandle-lcm-event.%s";
+ private static final String EVENT_TYPE_TEMPLATE = "org.onap.ncmp.cmhandle-lcm-event.%s";
LcmEventType(final String eventName) {
- this.eventName = String.format(eventTypeTemplate, eventName);
+ this.eventName = String.format(EVENT_TYPE_TEMPLATE, eventName);
}
public String getEventType() {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsService.java
index 10aebfa45d..192667175e 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsService.java
@@ -20,13 +20,18 @@
package org.onap.cps.ncmp.impl.inventory.sync.lcm;
-import io.micrometer.core.annotation.Timed;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.Timer;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.events.EventsPublisher;
import org.onap.cps.ncmp.events.lcm.v1.LcmEvent;
import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader;
+import org.onap.cps.ncmp.events.lcm.v1.Values;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.KafkaException;
@@ -41,8 +46,12 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor
public class LcmEventsService {
+ private static final Tag TAG_METHOD = Tag.of("method", "publishLcmEvent");
+ private static final Tag TAG_CLASS = Tag.of("class", LcmEventsService.class.getName());
+ private static final String UNAVAILABLE_CM_HANDLE_STATE = "N/A";
private final EventsPublisher<LcmEvent> eventsPublisher;
private final JsonObjectMapper jsonObjectMapper;
+ private final MeterRegistry meterRegistry;
@Value("${app.lcm.events.topic:ncmp-events}")
private String topicName;
@@ -51,24 +60,58 @@ public class LcmEventsService {
private boolean notificationsEnabled;
/**
- * Publish the LcmEvent with header to the public topic.
+ * Publishes an LCM event to the dedicated topic with optional notification headers.
+ * Capture and log KafkaException If an error occurs while publishing the event to Kafka
*
- * @param cmHandleId Cm Handle Id
- * @param lcmEvent Lcm Event
- * @param lcmEventHeader Lcm Event Header
+ * @param cmHandleId Cm Handle Id associated with the LCM event
+ * @param lcmEvent The LCM event object to be published
+ * @param lcmEventHeader Optional headers associated with the LCM event
*/
- @Timed(value = "cps.ncmp.lcm.events.publish", description = "Time taken to publish a LCM event")
public void publishLcmEvent(final String cmHandleId, final LcmEvent lcmEvent, final LcmEventHeader lcmEventHeader) {
+
if (notificationsEnabled) {
+ final Timer.Sample timerSample = Timer.start(meterRegistry);
try {
final Map<String, Object> lcmEventHeadersMap =
jsonObjectMapper.convertToValueType(lcmEventHeader, Map.class);
eventsPublisher.publishEvent(topicName, cmHandleId, lcmEventHeadersMap, lcmEvent);
} catch (final KafkaException e) {
log.error("Unable to publish message to topic : {} and cause : {}", topicName, e.getMessage());
+ } finally {
+ recordMetrics(lcmEvent, timerSample);
}
} else {
log.debug("Notifications disabled.");
}
}
+
+ private void recordMetrics(final LcmEvent lcmEvent, final Timer.Sample timerSample) {
+ final List<Tag> tags = new ArrayList<>(4);
+ tags.add(TAG_CLASS);
+ tags.add(TAG_METHOD);
+
+ final String oldCmHandleState = extractCmHandleStateValue(lcmEvent.getEvent().getOldValues());
+ tags.add(Tag.of("oldCmHandleState", oldCmHandleState));
+
+ final String newCmHandleState = extractCmHandleStateValue(lcmEvent.getEvent().getNewValues());
+ tags.add(Tag.of("newCmHandleState", newCmHandleState));
+
+ timerSample.stop(Timer.builder("cps.ncmp.lcm.events.publish")
+ .description("Time taken to publish a LCM event")
+ .tags(tags)
+ .register(meterRegistry));
+ }
+
+ /**
+ * Extracts the CM handle state value from the given Values object.
+ * If the provided Values object or its CM handle state is null, returns a default value.
+ *
+ * @param values The Values object containing CM handle state information.
+ * @return The CM handle state value as a string, or a default value if null.
+ */
+ private String extractCmHandleStateValue(final Values values) {
+ return (values != null && values.getCmHandleState() != null)
+ ? values.getCmHandleState().value()
+ : UNAVAILABLE_CM_HANDLE_STATE;
+ }
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumer.java
index 617fe7f01d..efcbb78ace 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumer.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumer.java
@@ -52,7 +52,7 @@ public class DeviceTrustLevelMessageConsumer {
final DeviceTrustLevel deviceTrustLevel =
CloudEventMapper.toTargetEvent(consumerRecord.value(), DeviceTrustLevel.class);
final String trustLevelAsString = deviceTrustLevel.getData().getTrustLevel();
- trustLevelManager.handleUpdateOfDeviceTrustLevel(cmHandleId, TrustLevel.valueOf(trustLevelAsString));
+ trustLevelManager.updateCmHandleTrustLevel(cmHandleId, TrustLevel.valueOf(trustLevelAsString));
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDog.java
index c81e9b7840..7581c4af7a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDog.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDog.java
@@ -26,9 +26,9 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.inventory.models.TrustLevel;
import org.onap.cps.ncmp.impl.dmi.DmiRestClient;
-import org.onap.cps.ncmp.impl.dmi.DmiServiceUrlTemplateBuilder;
-import org.onap.cps.ncmp.impl.dmi.UrlTemplateParameters;
import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService;
+import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@@ -67,13 +67,13 @@ public class DmiPluginTrustLevelWatchDog {
} else {
final Collection<String> cmHandleIds =
cmHandleQueryService.getCmHandleIdsByDmiPluginIdentifier(dmiServiceName);
- trustLevelManager.handleUpdateOfDmiTrustLevel(dmiServiceName, cmHandleIds, newDmiTrustLevel);
+ trustLevelManager.updateDmi(dmiServiceName, cmHandleIds, newDmiTrustLevel);
}
});
}
private String getDmiHealthStatus(final String dmiServiceBaseUrl) {
- final UrlTemplateParameters urlTemplateParameters = DmiServiceUrlTemplateBuilder.newInstance()
+ final UrlTemplateParameters urlTemplateParameters = RestServiceUrlTemplateBuilder.newInstance()
.createUrlTemplateParametersForHealthCheck(dmiServiceBaseUrl);
return dmiRestClient.getDmiHealthStatus(urlTemplateParameters).block();
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java
index b64d3a24b3..06ca67e57e 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java
@@ -22,8 +22,8 @@ package org.onap.cps.ncmp.impl.inventory.trustlevel;
import com.hazelcast.config.MapConfig;
import java.util.Map;
-import org.onap.cps.cache.HazelcastCacheConfig;
import org.onap.cps.ncmp.api.inventory.models.TrustLevel;
+import org.onap.cps.ncmp.impl.cache.HazelcastCacheConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -46,8 +46,7 @@ public class TrustLevelCacheConfig extends HazelcastCacheConfig {
*/
@Bean(TRUST_LEVEL_PER_CM_HANDLE)
public Map<String, TrustLevel> trustLevelPerCmHandle() {
- return createHazelcastInstance("hazelcastInstanceTrustLevelPerCmHandleMap",
- trustLevelPerCmHandleCacheConfig).getMap(TRUST_LEVEL_PER_CM_HANDLE);
+ return getOrCreateHazelcastInstance(trustLevelPerCmHandleCacheConfig).getMap(TRUST_LEVEL_PER_CM_HANDLE);
}
/**
@@ -57,7 +56,7 @@ public class TrustLevelCacheConfig extends HazelcastCacheConfig {
*/
@Bean(TRUST_LEVEL_PER_DMI_PLUGIN)
public Map<String, TrustLevel> trustLevelPerDmiPlugin() {
- return createHazelcastInstance("hazelcastInstanceTrustLevelPerDmiPluginMap",
+ return getOrCreateHazelcastInstance(
trustLevelPerDmiPluginCacheConfig).getMap(TRUST_LEVEL_PER_DMI_PLUGIN);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java
index 44079c0bc9..9b0cc3a0c0 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java
@@ -24,6 +24,7 @@ import java.util.Collection;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration;
import org.onap.cps.ncmp.api.inventory.models.TrustLevel;
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence;
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
@@ -49,11 +50,26 @@ public class TrustLevelManager {
private static final String AVC_NO_OLD_VALUE = null;
/**
+ * Add dmi plugins to the cache.
+ *
+ * @param dmiPluginRegistration a dmi plugin being registered
+ */
+ public void registerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) {
+ final String dmiServiceName;
+ if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) {
+ dmiServiceName = dmiPluginRegistration.getDmiPlugin();
+ } else {
+ dmiServiceName = dmiPluginRegistration.getDmiDataPlugin();
+ }
+ trustLevelPerDmiPlugin.put(dmiServiceName, TrustLevel.COMPLETE);
+ }
+
+ /**
* Add cmHandles to the cache and publish notification for initial trust level of cmHandles if it is NONE.
*
* @param cmHandlesToBeCreated a list of cmHandles being created
*/
- public void handleInitialRegistrationOfTrustLevels(final Map<String, TrustLevel> cmHandlesToBeCreated) {
+ public void registerCmHandles(final Map<String, TrustLevel> cmHandlesToBeCreated) {
for (final Map.Entry<String, TrustLevel> entry : cmHandlesToBeCreated.entrySet()) {
final String cmHandleId = entry.getKey();
if (trustLevelPerCmHandle.containsKey(cmHandleId)) {
@@ -82,15 +98,15 @@ public class TrustLevelManager {
* @param affectedCmHandleIds cm handle ids belonging to dmi service name
* @param newDmiTrustLevel new trust level of the dmi plugin
*/
- public void handleUpdateOfDmiTrustLevel(final String dmiServiceName,
- final Collection<String> affectedCmHandleIds,
- final TrustLevel newDmiTrustLevel) {
+ public void updateDmi(final String dmiServiceName,
+ final Collection<String> affectedCmHandleIds,
+ final TrustLevel newDmiTrustLevel) {
final TrustLevel oldDmiTrustLevel = trustLevelPerDmiPlugin.get(dmiServiceName);
trustLevelPerDmiPlugin.put(dmiServiceName, newDmiTrustLevel);
for (final String affectedCmHandleId : affectedCmHandleIds) {
- final TrustLevel deviceTrustLevel = trustLevelPerCmHandle.get(affectedCmHandleId);
- final TrustLevel oldEffectiveTrustLevel = deviceTrustLevel.getEffectiveTrustLevel(oldDmiTrustLevel);
- final TrustLevel newEffectiveTrustLevel = deviceTrustLevel.getEffectiveTrustLevel(newDmiTrustLevel);
+ final TrustLevel cmHandleTrustLevel = trustLevelPerCmHandle.get(affectedCmHandleId);
+ final TrustLevel oldEffectiveTrustLevel = cmHandleTrustLevel.getEffectiveTrustLevel(oldDmiTrustLevel);
+ final TrustLevel newEffectiveTrustLevel = cmHandleTrustLevel.getEffectiveTrustLevel(newDmiTrustLevel);
sendAvcNotificationIfRequired(affectedCmHandleId, oldEffectiveTrustLevel, newEffectiveTrustLevel);
}
}
@@ -100,23 +116,52 @@ public class TrustLevelManager {
* changed.
*
* @param cmHandleId cm handle id
- * @param newDeviceTrustLevel new trust level of the device
+ * @param newCmHandleTrustLevel new trust level of the device
*/
- public void handleUpdateOfDeviceTrustLevel(final String cmHandleId,
- final TrustLevel newDeviceTrustLevel) {
- final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
- final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA);
+ public void updateCmHandleTrustLevel(final String cmHandleId,
+ final TrustLevel newCmHandleTrustLevel) {
+ final String dmiServiceName = getDmiServiceName(cmHandleId);
final TrustLevel dmiTrustLevel = trustLevelPerDmiPlugin.get(dmiServiceName);
- final TrustLevel oldDeviceTrustLevel = trustLevelPerCmHandle.get(cmHandleId);
+ final TrustLevel oldCmHandleTrustLevel = trustLevelPerCmHandle.get(cmHandleId);
- final TrustLevel oldEffectiveTrustLevel = oldDeviceTrustLevel.getEffectiveTrustLevel(dmiTrustLevel);
- final TrustLevel newEffectiveTrustLevel = newDeviceTrustLevel.getEffectiveTrustLevel(dmiTrustLevel);
+ final TrustLevel oldEffectiveTrustLevel = oldCmHandleTrustLevel.getEffectiveTrustLevel(dmiTrustLevel);
+ final TrustLevel newEffectiveTrustLevel = newCmHandleTrustLevel.getEffectiveTrustLevel(dmiTrustLevel);
- trustLevelPerCmHandle.put(cmHandleId, newDeviceTrustLevel);
+ trustLevelPerCmHandle.put(cmHandleId, newCmHandleTrustLevel);
sendAvcNotificationIfRequired(cmHandleId, oldEffectiveTrustLevel, newEffectiveTrustLevel);
}
+ /**
+ * Select effective trust level among device and dmi plugin.
+ *
+ * @param cmHandleId cm handle id
+ * @return TrustLevel effective trust level
+ */
+ public TrustLevel getEffectiveTrustLevel(final String cmHandleId) {
+ final TrustLevel dmiTrustLevel = TrustLevel.COMPLETE; // TODO: CPS-2375
+ final TrustLevel cmHandleTrustLevel = trustLevelPerCmHandle.getOrDefault(cmHandleId, TrustLevel.NONE);
+ return dmiTrustLevel.getEffectiveTrustLevel(cmHandleTrustLevel);
+ }
+
+ /**
+ * Remove cm handle trust level from the cache.
+ *
+ * @param cmHandleIds cm handle ids to be removed from the cache
+ */
+ public void removeCmHandles(final Collection<String> cmHandleIds) {
+ for (final String cmHandleId : cmHandleIds) {
+ if (trustLevelPerCmHandle.remove(cmHandleId) == null) {
+ log.debug("Removed Cm handle: {} is not in trust level cache", cmHandleId);
+ }
+ }
+ }
+
+ private String getDmiServiceName(final String cmHandleId) {
+ final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
+ return yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA);
+ }
+
private void sendAvcNotificationIfRequired(final String notificationCandidateCmHandleId,
final TrustLevel oldEffectiveTrustLevel,
final TrustLevel newEffectiveTrustLevel) {
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/config/NcmpRequestLoggingConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfiguration.java
index e91f48f78a..333030ccd2 100644
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/config/NcmpRequestLoggingConfig.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfiguration.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2024 Nordix Foundation.
+ * Copyright (C) 2024 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,28 +18,29 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.dmi.rest.stub.config;
+package org.onap.cps.ncmp.impl.policyexecutor;
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.config.PolicyExecutorHttpClientConfig;
+import org.onap.cps.ncmp.impl.utils.http.WebClientConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.web.filter.CommonsRequestLoggingFilter;
+import org.springframework.web.reactive.function.client.WebClient;
@Configuration
-public class NcmpRequestLoggingConfig {
+@RequiredArgsConstructor
+public class PolicyExecutorWebClientConfiguration extends WebClientConfiguration {
+
+ private final PolicyExecutorHttpClientConfig policyExecutorHttpClientConfig;
/**
- * Configuration class to log NCMP request headers and payload.
- * logged request information before it is processed.
+ * Configures and creates a web client bean for Policy Executor.
+ *
+ * @param webClientBuilder The builder instance to create the WebClient.
+ * @return a WebClient instance configured for Policy Executor.
*/
@Bean
- public CommonsRequestLoggingFilter logNcmpRequestInfo() {
- final CommonsRequestLoggingFilter commonsRequestLoggingFilter = new CommonsRequestLoggingFilter();
- commonsRequestLoggingFilter.setIncludeHeaders(true);
- commonsRequestLoggingFilter.setIncludeQueryString(true);
- commonsRequestLoggingFilter.setIncludePayload(true);
- commonsRequestLoggingFilter.setMaxPayloadLength(1000);
- commonsRequestLoggingFilter.setAfterMessagePrefix("NCMP REQUEST DATA: ");
- return commonsRequestLoggingFilter;
+ public WebClient policyExecutorWebClient(final WebClient.Builder webClientBuilder) {
+ return configureWebClient(webClientBuilder, policyExecutorHttpClientConfig.getAllServices());
}
}
-
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java
index 832e576d55..c526dfb297 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java
@@ -56,6 +56,21 @@ public class AlternateIdMatcher {
throw new NoAlternateIdMatchFoundException(alternateId);
}
+ /**
+ * Get cm handle Id from given cmHandleReference.
+ *
+ * @param cmHandleReference cm handle or alternate identifier
+ * @return cm handle id string
+ */
+ public String getCmHandleId(final String cmHandleReference) {
+ if (inventoryPersistence.isExistingCmHandleId(cmHandleReference)) {
+ return cmHandleReference;
+ } else {
+ return inventoryPersistence.getCmHandleDataNodeByAlternateId(cmHandleReference)
+ .getLeaves().get("id").toString();
+ }
+ }
+
private String getParentPath(final String path, final String separator) {
final int lastSeparatorIndex = path.lastIndexOf(separator);
return lastSeparatorIndex < 0 ? "" : path.substring(0, lastSeparatorIndex);
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiServiceUrlTemplateBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java
index e7dbea83f2..c850ca94a0 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiServiceUrlTemplateBuilder.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.impl.dmi;
+package org.onap.cps.ncmp.impl.utils.http;
import java.util.Collections;
import java.util.HashMap;
@@ -30,7 +30,7 @@ import org.apache.logging.log4j.util.Strings;
import org.springframework.web.util.UriComponentsBuilder;
@NoArgsConstructor
-public class DmiServiceUrlTemplateBuilder {
+public class RestServiceUrlTemplateBuilder {
private final UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance();
private static final String FIXED_PATH_SEGMENT = null;
@@ -43,8 +43,8 @@ public class DmiServiceUrlTemplateBuilder {
*
* @return a new instance of DmiServiceUrlTemplateBuilder
*/
- public static DmiServiceUrlTemplateBuilder newInstance() {
- return new DmiServiceUrlTemplateBuilder();
+ public static RestServiceUrlTemplateBuilder newInstance() {
+ return new RestServiceUrlTemplateBuilder();
}
/**
@@ -53,7 +53,7 @@ public class DmiServiceUrlTemplateBuilder {
* @param pathSegment the path segment
* @return this builder instance
*/
- public DmiServiceUrlTemplateBuilder fixedPathSegment(final String pathSegment) {
+ public RestServiceUrlTemplateBuilder fixedPathSegment(final String pathSegment) {
pathSegments.put(pathSegment, FIXED_PATH_SEGMENT);
return this;
}
@@ -66,7 +66,7 @@ public class DmiServiceUrlTemplateBuilder {
* @param value the value to be insert in teh URL for the given variable path segment
* @return this builder instance
*/
- public DmiServiceUrlTemplateBuilder variablePathSegment(final String pathSegment, final String value) {
+ public RestServiceUrlTemplateBuilder variablePathSegment(final String pathSegment, final String value) {
pathSegments.put(pathSegment, value);
return this;
}
@@ -80,8 +80,8 @@ public class DmiServiceUrlTemplateBuilder {
*
* @return this builder instance
*/
- public DmiServiceUrlTemplateBuilder queryParameter(final String queryParameterName,
- final String queryParameterValue) {
+ public RestServiceUrlTemplateBuilder queryParameter(final String queryParameterName,
+ final String queryParameterValue) {
if (Strings.isNotBlank(queryParameterValue)) {
queryParameters.put(queryParameterName, queryParameterValue);
}
@@ -91,14 +91,12 @@ public class DmiServiceUrlTemplateBuilder {
/**
* Constructs a URL template with variables based on the accumulated path segments and query parameters.
*
- * @param dmiServiceBaseUrl the base URL of the DMI service, e.g., "http://dmi-service.com".
- * @param dmiBasePath the base path of the DMI service
+ * @param serviceBaseUrl the base URL of the service, e.g., "http://dmi-service.com".
+ * @param basePath the base path of the service
* @return a UrlTemplateParameters instance containing the complete URL template and URL variables
*/
- public UrlTemplateParameters createUrlTemplateParameters(final String dmiServiceBaseUrl, final String dmiBasePath) {
- this.uriComponentsBuilder.pathSegment(dmiBasePath)
- .pathSegment(VERSION_SEGMENT);
-
+ public UrlTemplateParameters createUrlTemplateParameters(final String serviceBaseUrl, final String basePath) {
+ this.uriComponentsBuilder.pathSegment(basePath).pathSegment(VERSION_SEGMENT);
final Map<String, String> urlTemplateVariables = new HashMap<>();
pathSegments.forEach((pathSegmentName, variablePathValue) -> {
@@ -115,22 +113,21 @@ public class DmiServiceUrlTemplateBuilder {
urlTemplateVariables.put(paramName, paramValue);
});
- final String urlTemplate = dmiServiceBaseUrl + this.uriComponentsBuilder.build().toUriString();
+ final String urlTemplate = serviceBaseUrl + this.uriComponentsBuilder.build().toUriString();
return new UrlTemplateParameters(urlTemplate, urlTemplateVariables);
}
/**
- * Constructs a URL for DMI health check based on the given base URL.
+ * Constructs a URL for a spring actuator health check based on the given base URL.
*
- * @param dmiServiceBaseUrl the base URL of the DMI service, e.g., "http://dmi-service.com".
+ * @param serviceBaseUrl the base URL of the service, e.g., "http://dmi-service.com".
* @return a {@link UrlTemplateParameters} instance containing the complete URL template and empty URL variables,
* suitable for DMI health check.
*/
- public UrlTemplateParameters createUrlTemplateParametersForHealthCheck(final String dmiServiceBaseUrl) {
- this.uriComponentsBuilder.pathSegment("actuator")
- .pathSegment("health");
+ public UrlTemplateParameters createUrlTemplateParametersForHealthCheck(final String serviceBaseUrl) {
+ this.uriComponentsBuilder.pathSegment("actuator").pathSegment("health");
- final String urlTemplate = dmiServiceBaseUrl + this.uriComponentsBuilder.build().toUriString();
+ final String urlTemplate = serviceBaseUrl + this.uriComponentsBuilder.build().toUriString();
return new UrlTemplateParameters(urlTemplate, Collections.emptyMap());
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/UrlTemplateParameters.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/UrlTemplateParameters.java
index f51511116a..839af71823 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/UrlTemplateParameters.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/UrlTemplateParameters.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.impl.dmi;
+package org.onap.cps.ncmp.impl.utils.http;
import java.util.Map;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/WebClientConfiguration.java
index c176e40226..8ae942eb7b 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientConfiguration.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/WebClientConfiguration.java
@@ -18,19 +18,15 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.impl.dmi;
+package org.onap.cps.ncmp.impl.utils.http;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import io.netty.resolver.DefaultAddressResolverGroup;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
-import lombok.RequiredArgsConstructor;
-import org.onap.cps.ncmp.config.HttpClientConfiguration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
+import org.onap.cps.ncmp.config.ServiceConfig;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
@@ -39,58 +35,22 @@ import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
/**
- * Configures and creates WebClient beans for various DMI services including data, model, and health check services.
+ * Configures and creates WebClient beans for various rest services such as DMI and Policy Executor.
* The configuration utilizes Netty-based HttpClient with custom connection settings, read and write timeouts,
* and initializes WebClient with these settings to ensure optimal performance and resource management.
*/
-@Configuration
-@RequiredArgsConstructor
-public class DmiWebClientConfiguration {
+public class WebClientConfiguration {
- private final HttpClientConfiguration httpClientConfiguration;
private static final Duration DEFAULT_RESPONSE_TIMEOUT = Duration.ofSeconds(30);
- /**
- * Configures and creates a web client bean for DMI data services.
- *
- * @param webClientBuilder The builder instance to create the WebClient.
- * @return a WebClient instance configured for data services.
- */
- @Bean
- public WebClient dataServicesWebClient(final WebClient.Builder webClientBuilder) {
- return configureWebClient(webClientBuilder, httpClientConfiguration.getDataServices());
- }
-
- /**
- * Configures and creates a web client bean for DMI model services.
- *
- * @param webClientBuilder The builder instance to create the WebClient.
- * @return a WebClient instance configured for model services.
- */
- @Bean
- public WebClient modelServicesWebClient(final WebClient.Builder webClientBuilder) {
- return configureWebClient(webClientBuilder, httpClientConfiguration.getModelServices());
- }
-
- /**
- * Configures and creates a web client bean for DMI health check services.
- *
- * @param webClientBuilder The builder instance to create the WebClient.
- * @return a WebClient instance configured for health check services.
- */
- @Bean
- public WebClient healthChecksWebClient(final WebClient.Builder webClientBuilder) {
- return configureWebClient(webClientBuilder, httpClientConfiguration.getHealthCheckServices());
- }
-
- private WebClient configureWebClient(final WebClient.Builder webClientBuilder,
- final HttpClientConfiguration.ServiceConfig serviceConfig) {
+ protected WebClient configureWebClient(final WebClient.Builder webClientBuilder,
+ final ServiceConfig serviceConfig) {
final ConnectionProvider connectionProvider = getConnectionProvider(serviceConfig);
final HttpClient httpClient = createHttpClient(serviceConfig, connectionProvider);
return buildAndGetWebClient(webClientBuilder, httpClient, serviceConfig.getMaximumInMemorySizeInMegabytes());
}
- private static HttpClient createHttpClient(final HttpClientConfiguration.ServiceConfig serviceConfig,
+ private static HttpClient createHttpClient(final ServiceConfig serviceConfig,
final ConnectionProvider connectionProvider) {
return HttpClient.create(connectionProvider)
.responseTimeout(DEFAULT_RESPONSE_TIMEOUT)
@@ -102,8 +62,7 @@ public class DmiWebClientConfiguration {
.compress(true);
}
- @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE")
- private static ConnectionProvider getConnectionProvider(final HttpClientConfiguration.ServiceConfig serviceConfig) {
+ private static ConnectionProvider getConnectionProvider(final ServiceConfig serviceConfig) {
return ConnectionProvider.builder(serviceConfig.getConnectionProviderName())
.maxConnections(serviceConfig.getMaximumConnectionsTotal())
.pendingAcquireMaxCount(serviceConfig.getPendingAcquireMaxCount())
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CmAvcEventPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CmAvcEventPublisher.java
index 06269d5177..2a9717cc1a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CmAvcEventPublisher.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CmAvcEventPublisher.java
@@ -29,7 +29,6 @@ import org.onap.cps.events.EventsPublisher;
import org.onap.cps.ncmp.events.avc.ncmp_to_client.Avc;
import org.onap.cps.ncmp.events.avc.ncmp_to_client.AvcEvent;
import org.onap.cps.ncmp.events.avc.ncmp_to_client.Data;
-import org.onap.cps.ncmp.utils.events.NcmpEvent;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/HttpClientConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/DmiHttpClientConfigSpec.groovy
index 1d3b22fb73..23f5edd890 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/HttpClientConfigurationSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/DmiHttpClientConfigSpec.groovy
@@ -28,46 +28,46 @@ import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
@SpringBootTest
-@ContextConfiguration(classes = [HttpClientConfiguration])
-@EnableConfigurationProperties(HttpClientConfiguration)
-class HttpClientConfigurationSpec extends Specification {
+@ContextConfiguration(classes = [DmiHttpClientConfig])
+@EnableConfigurationProperties
+class DmiHttpClientConfigSpec extends Specification {
@Autowired
- HttpClientConfiguration httpClientConfiguration
+ DmiHttpClientConfig dmiHttpClientConfig
def 'Test http client configuration properties of data with custom and default values'() {
- expect: 'properties are populated correctly for data'
- with(httpClientConfiguration.dataServices) {
- assert connectionTimeoutInSeconds == 123
- assert readTimeoutInSeconds == 33
- assert writeTimeoutInSeconds == 30
- assert maximumConnectionsTotal == 100
- assert pendingAcquireMaxCount == 22
- assert maximumInMemorySizeInMegabytes == 7
+ expect: 'properties are populated correctly for data services'
+ with(dmiHttpClientConfig.dataServices) {
+ assert maximumInMemorySizeInMegabytes == 1
+ assert maximumConnectionsTotal == 2
+ assert pendingAcquireMaxCount == 3
+ assert connectionTimeoutInSeconds == 4
+ assert readTimeoutInSeconds == 5
+ assert writeTimeoutInSeconds == 6
}
}
def 'Test http client configuration properties of model with custom and default values'() {
- expect: 'properties are populated correctly for model'
- with(httpClientConfiguration.modelServices) {
- assert connectionTimeoutInSeconds == 456
- assert readTimeoutInSeconds == 30
- assert writeTimeoutInSeconds == 30
- assert maximumConnectionsTotal == 111
- assert pendingAcquireMaxCount == 44
- assert maximumInMemorySizeInMegabytes == 8
+ expect: 'properties are populated correctly for model services'
+ with(dmiHttpClientConfig.modelServices) {
+ assert maximumInMemorySizeInMegabytes == 11
+ assert maximumConnectionsTotal == 12
+ assert pendingAcquireMaxCount == 13
+ assert connectionTimeoutInSeconds == 14
+ assert readTimeoutInSeconds == 15
+ assert writeTimeoutInSeconds == 16
}
}
def 'Test http client configuration properties of health with default values'() {
- expect: 'properties are populated correctly for health'
- with(httpClientConfiguration.healthCheckServices) {
- assert connectionTimeoutInSeconds == 30
- assert readTimeoutInSeconds == 30
- assert writeTimeoutInSeconds == 30
- assert maximumConnectionsTotal == 10
- assert pendingAcquireMaxCount == 5
- assert maximumInMemorySizeInMegabytes == 1
+ expect: 'properties are populated correctly for health check services'
+ with(dmiHttpClientConfig.healthCheckServices) {
+ assert maximumInMemorySizeInMegabytes == 21
+ assert maximumConnectionsTotal == 22
+ assert pendingAcquireMaxCount == 23
+ assert connectionTimeoutInSeconds == 24
+ assert readTimeoutInSeconds == 25
+ assert writeTimeoutInSeconds == 26
}
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy
deleted file mode 100644
index 0f6906942f..0000000000
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2024 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.config
-
-import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter
-import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
-import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler
-import org.spockframework.spring.SpringBean
-import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer
-import spock.lang.Shared
-import spock.lang.Specification
-
-class OpenTelemetryConfigSpec extends Specification{
-
- @Shared
- @SpringBean
- OpenTelemetryConfig openTelemetryConfig = new OpenTelemetryConfig()
-
- def setupSpec() {
- openTelemetryConfig.tracingExporterEndpointUrl="http://tracingExporterEndpointUrl"
- openTelemetryConfig.jaegerRemoteSamplerUrl="http://jaegerremotesamplerurl"
- openTelemetryConfig.serviceId ="cps-application"
- }
-
- def 'OpenTelemetryConfig Construction.'() {
- expect: 'the system can create an instance'
- new OpenTelemetryConfig() != null
- }
-
- def 'OTLP Exporter creation with Grpc protocol'(){
- when: 'an OTLP exporter is created'
- def result = openTelemetryConfig.createOtlpExporterGrpc()
- then: 'an OTLP Exporter is created'
- assert result instanceof OtlpGrpcSpanExporter
- }
-
- def 'OTLP Exporter creation with HTTP protocol'(){
- when: 'an OTLP exporter is created'
- def result = openTelemetryConfig.createOtlpExporterHttp()
- then: 'an OTLP Exporter is created'
- assert result instanceof OtlpHttpSpanExporter
- and:
- assert result.builder.endpoint=="http://tracingExporterEndpointUrl"
- }
-
- def 'Jaeger Remote Sampler Creation'(){
- when: 'an OTLP exporter is created'
- def result = openTelemetryConfig.createJaegerRemoteSampler()
- then: 'an OTLP Exporter is created'
- assert result instanceof JaegerRemoteSampler
- and:
- assert result.delegate.type=="remoteSampling"
- and:
- assert result.delegate.url.toString().startsWith("http://jaegerremotesamplerurl")
- }
-
- def 'Skipping Acutator endpoints'(){
- when: 'an OTLP exporter is created'
- def result = openTelemetryConfig.skipActuatorEndpointsFromObservation()
- then: 'an OTLP Exporter is created'
- assert result instanceof ObservationRegistryCustomizer
- }
-}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy
new file mode 100644
index 0000000000..cbff73113e
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy
@@ -0,0 +1,113 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.config
+
+import io.micrometer.observation.ObservationPredicate
+import io.micrometer.observation.ObservationRegistry
+import io.micrometer.observation.ObservationRegistry.ObservationConfig
+import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter
+import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
+import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.http.server.observation.ServerRequestObservationContext
+import org.springframework.mock.web.MockHttpServletRequest
+import org.springframework.util.AntPathMatcher
+import spock.lang.Specification
+
+@SpringBootTest(classes = [OpenTelemetryConfig])
+class OpenTelemetryConfigSpec extends Specification {
+
+ def objectUnderTest
+
+ @Value('${cps.tracing.exporter.endpoint}')
+ def tracingExporterEndpointUrl
+
+ @Value('${cps.tracing.sampler.jaeger_remote.endpoint}')
+ def jaegerRemoteSamplerUrl
+
+ def setup() {
+ objectUnderTest = new OpenTelemetryConfig(
+ serviceId: 'sample-app',
+ tracingExporterEndpointUrl: tracingExporterEndpointUrl,
+ jaegerRemoteSamplerUrl: jaegerRemoteSamplerUrl,
+ excludedObservationNames: ['excluded-task-name'])
+ }
+
+ def 'OTLP exporter creation with Grpc protocol'() {
+ when: 'an OTLP exporter is created'
+ def result = objectUnderTest.createOtlpExporterGrpc()
+ then: 'expected an instance of OtlpGrpcSpanExporter'
+ assert result instanceof OtlpGrpcSpanExporter
+ }
+
+ def 'OTLP exporter creation with HTTP protocol'() {
+ when: 'an OTLP exporter is created'
+ def result = objectUnderTest.createOtlpExporterHttp()
+ then: 'an OTLP Exporter is created'
+ assert result instanceof OtlpHttpSpanExporter
+ and: 'the endpoint is correctly set'
+ assert result.builder.endpoint == 'http://exporter-test-url'
+ }
+
+ def 'Jaeger Remote Sampler Creation'() {
+ when: 'a Jaeger remote sampler is created'
+ def result = objectUnderTest.createJaegerRemoteSampler()
+ then: 'a Jaeger remote sampler is created'
+ assert result instanceof JaegerRemoteSampler
+ and: 'the sampler type is correct'
+ assert result.delegate.type == 'remoteSampling'
+ and: 'the sampler endpoint is correctly set'
+ assert result.delegate.url.toString().startsWith('http://jaeger-remote-test-url')
+ }
+
+ def 'Skipping actuator endpoints'() {
+ given: 'a mocked observation registry and config'
+ def observationRegistry = Mock(ObservationRegistry.class)
+ def observationConfig = Mock(ObservationConfig.class)
+ observationRegistry.observationConfig() >> observationConfig
+ when: 'an observation registry customizer is created and applied'
+ def result = objectUnderTest.skipActuatorEndpointsFromObservation()
+ result.customize(observationRegistry)
+ then: 'the observation predicate is set correctly'
+ 1 * observationConfig.observationPredicate(_) >> { ObservationPredicate observationPredicate ->
+ def mockedHttpServletRequest = new MockHttpServletRequest(_ as String, requestUrl)
+ def serverRequestObservationContext = new ServerRequestObservationContext(mockedHttpServletRequest, null)
+ and: 'expected predicate for endpoint'
+ assert observationPredicate.test('some-name', serverRequestObservationContext) == expectedPredicate
+ }
+ where: 'the following parameters are used'
+ scenario | requestUrl || expectedPredicate
+ 'an actuator' | '/actuator' || false
+ 'a non actuator' | '/some-api' || true
+ }
+
+ def 'Observation predicate is configured to filter out excluded tasks by name'() {
+ when: 'a path matcher and observation predicate'
+ def observationPredicate = objectUnderTest.observationPredicate(new AntPathMatcher('/'))
+ then: 'a task name is provided'
+ assert observationPredicate.test(taskName, null) == expectedPredicate
+ where: 'the following parameters are used'
+ taskName || expectedPredicate
+ 'excluded-task-name' || false
+ 'non-excluded-task-name' || true
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy
new file mode 100644
index 0000000000..b988f9e171
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.config
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.context.properties.EnableConfigurationProperties
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
+import spock.lang.Specification
+
+@SpringBootTest
+@ContextConfiguration(classes = [PolicyExecutorHttpClientConfig])
+@EnableConfigurationProperties
+class PolicyExecutorHttpClientConfigSpec extends Specification {
+
+ @Autowired
+ PolicyExecutorHttpClientConfig policyExecutorHttpClientConfig
+
+ def 'Http client configuration properties for policy executor http client.'() {
+ expect: 'properties are populated correctly for all services'
+ with(policyExecutorHttpClientConfig.allServices) {
+ assert maximumInMemorySizeInMegabytes == 31
+ assert maximumConnectionsTotal == 32
+ assert pendingAcquireMaxCount == 33
+ assert connectionTimeoutInSeconds == 34
+ assert writeTimeoutInSeconds == 36
+ }
+ }
+
+ def 'Increased read timeout.'() {
+ expect: 'Read timeout is 10 seconds more then configured to enable a separate timeout method in policy executor with the required timeout'
+ assert policyExecutorHttpClientConfig.allServices.readTimeoutInSeconds == 35 + 10
+
+ }
+}
diff --git a/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfigSpec.groovy
index 022cd74ea6..0bd838437d 100644
--- a/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfigSpec.groovy
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.cache
+package org.onap.cps.ncmp.impl.cache
import com.hazelcast.config.Config
import com.hazelcast.config.RestEndpointGroup
@@ -29,29 +29,24 @@ class HazelcastCacheConfigSpec extends Specification {
def objectUnderTest = new HazelcastCacheConfig()
def 'Create Hazelcast instance with a #scenario'() {
- given: 'a cluster name'
+ given: 'a cluster name and instance config name'
objectUnderTest.clusterName = 'my cluster'
- when: 'an hazelcast instance is created (name has to be unique)'
- def result = objectUnderTest.createHazelcastInstance(scenario, config)
+ objectUnderTest.instanceConfigName = 'my instance config'
+ when: 'a hazelcast instance is created (name has to be unique)'
+ def result = objectUnderTest.getOrCreateHazelcastInstance(config)
then: 'the instance is created and has the correct name'
- assert result.name == scenario
+ assert result.name == 'my instance config'
and: 'if applicable it has a map config with the expected name'
if (expectMapConfig) {
assert result.config.mapConfigs.values()[0].name == 'my map config'
- } else {
- assert result.config.mapConfigs.isEmpty()
}
and: 'if applicable it has a queue config with the expected name'
if (expectQueueConfig) {
assert result.config.queueConfigs.values()[0].name == 'my queue config'
- } else {
- assert result.config.queueConfigs.isEmpty()
}
and: 'if applicable it has a set config with the expected name'
if (expectSetConfig) {
assert result.config.setConfigs.values()[0].name == 'my set config'
- } else {
- assert result.config.setConfigs.isEmpty()
}
where: 'the following configs are used'
scenario | config || expectMapConfig | expectQueueConfig | expectSetConfig
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfigSpec.groovy
index 915bccb53d..740567c4bf 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfigSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfigSpec.groovy
@@ -36,13 +36,17 @@ class CmSubscriptionConfigSpec extends Specification {
@Autowired
IMap<String, Map<String, DmiCmSubscriptionDetails>> cmNotificationSubscriptionCache;
+ def cleanupSpec() {
+ Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').shutdown()
+ }
+
def 'Embedded (hazelcast) cache for Cm Notification Subscription Cache.'() {
expect: 'system is able to create an instance of the Cm Notification Subscription Cache'
assert null != cmNotificationSubscriptionCache
and: 'there is at least 1 instance'
assert Hazelcast.allHazelcastInstances.size() > 0
and: 'Cm Notification Subscription Cache is present'
- assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceCmNotificationSubscription')
+ assert Hazelcast.allHazelcastInstances.name.contains('cps-and-ncmp-hazelcast-instance-test-config')
}
def 'Provided CM Subscription data'() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandlerSpec.groovy
index 2d50e770dd..791a154608 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandlerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandlerSpec.groovy
@@ -65,17 +65,34 @@ class DmiCacheHandlerSpec extends MessagingBaseSpec {
initialiseMockInventoryPersistenceResponses()
}
- def 'Load CM subscription event to cache'() {
- given: 'a valid subscription event with Id'
+ def 'Load CM subscription event to cache with predicates'() {
+ given: 'a subscription event with id'
def subscriptionId = ncmpInEvent.getData().getSubscriptionId()
and: 'list of predicates'
def predicates = ncmpInEvent.getData().getPredicates()
- when: 'a valid event object loaded in cache'
+ when: 'subscription is loaded to cache with predicates'
objectUnderTest.add(subscriptionId, predicates)
- then: 'the cache contains the correct entry with #subscriptionId subscription ID'
+ then: 'the number of entries in cache is correct'
+ assert testCache.size() == 1
+ and: 'the cache contains the correct entries'
assert testCache.containsKey(subscriptionId)
}
+ def 'Load CM subscription event to cache with dmi subscription details per dmi'() {
+ given: 'a subscription event with id'
+ def subscriptionId = ncmpInEvent.getData().getSubscriptionId()
+ and: 'dmi subscription details per dmi'
+ def dmiSubscriptionsPerDmi = [:]
+ when: 'subscription is loaded to cache with dmi subscription details per dmi'
+ objectUnderTest.add(subscriptionId, dmiSubscriptionsPerDmi)
+ then: 'the number of entries in cache is correct'
+ assert testCache.size() == 1
+ and: 'the cache contains the correct entries'
+ assert testCache.containsKey(subscriptionId)
+ and: 'the entry for the subscription ID matches the provided DMI subscription details'
+ assert testCache.get(subscriptionId) == dmiSubscriptionsPerDmi
+ }
+
def 'Get cache entry via subscription id'() {
given: 'the cache contains value for some-id'
testCache.put('some-id',[:])
@@ -157,7 +174,7 @@ class DmiCacheHandlerSpec extends MessagingBaseSpec {
def subscriptionId = ncmpInEvent.getData().getSubscriptionId()
objectUnderTest.add(subscriptionId, predicates)
when: 'subscription status per dmi is updated in cache'
- objectUnderTest.updateDmiSubscriptionStatusPerDmi(subscriptionId,'dmi-1', CmSubscriptionStatus.ACCEPTED)
+ objectUnderTest.updateDmiSubscriptionStatus(subscriptionId,'dmi-1', CmSubscriptionStatus.ACCEPTED)
then: 'verify status has been updated in cache'
def predicate = testCache.get(subscriptionId)
assert predicate.get('dmi-1').cmSubscriptionStatus == CmSubscriptionStatus.ACCEPTED
@@ -180,7 +197,7 @@ class DmiCacheHandlerSpec extends MessagingBaseSpec {
def subscriptionId = ncmpInEvent.getData().getSubscriptionId()
objectUnderTest.add(subscriptionId, predicates)
when: 'subscription is persisted in database'
- objectUnderTest.removeFromDatabasePerDmi(subscriptionId,'dmi-1')
+ objectUnderTest.removeFromDatabase(subscriptionId,'dmi-1')
then: 'persistence service is called the correct number of times per dmi'
4 * mockCmSubscriptionPersistenceService.removeCmSubscription(_,_,_,subscriptionId)
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumerSpec.groovy
index a8b5250edf..06651be913 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumerSpec.groovy
@@ -37,7 +37,6 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.annotation.DirtiesContext
import org.testcontainers.spock.Testcontainers
-
import java.time.Duration
import static org.onap.cps.ncmp.utils.events.CloudEventMapper.toTargetEvent
@@ -86,8 +85,8 @@ class CmAvcEventConsumerSpec extends MessagingBaseSpec {
def convertedAvcEvent = toTargetEvent(cloudEvent, AvcEvent.class)
and: 'we have correct headers forwarded where correlation id matches'
assert KafkaHeaders.getParsedKafkaHeader(record.headers(), 'ce_correlationid') == 'test-cmhandle1'
- and: 'event id differs(as per requirement) between consumed and forwarded'
- assert KafkaHeaders.getParsedKafkaHeader(record.headers(), 'ce_id') != 'sample-eventid'
+ and: 'event id is same between consumed and forwarded'
+ assert KafkaHeaders.getParsedKafkaHeader(record.headers(), 'ce_id') == 'sample-eventid'
and: 'the event payload still matches'
assert testEventSent == convertedAvcEvent
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapperSpec.groovy
new file mode 100644
index 0000000000..5d74f45bb9
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapperSpec.groovy
@@ -0,0 +1,52 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi
+
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionKey
+import spock.lang.Specification
+
+class DmiCmSubscriptionDetailsPerDmiMapperSpec extends Specification {
+
+ def objectUnderTest = new DmiCmSubscriptionDetailsPerDmiMapper()
+
+ def 'Check for grouping of Dmi Subscription Details'() {
+ given: 'details in the form of datastore , cmhandle and xpath'
+ def subscribersPerDmi = [
+ 'dmi-1': [
+ new DmiCmSubscriptionKey('ncmp-datastore:passthrough-operational', 'ch-1', '/a/b'),
+ new DmiCmSubscriptionKey('ncmp-datastore:passthrough-operational', 'ch-2', '/a/b')
+ ],
+ 'dmi-2': [
+ new DmiCmSubscriptionKey('ncmp-datastore:passthrough-running', 'ch-3', '/c/d'),
+ new DmiCmSubscriptionKey('ncmp-datastore:passthrough-running', 'ch-3', '/e/f')
+ ]
+ ]
+ when: 'we try to map the values based on datastore and xpath'
+ def result = objectUnderTest.toDmiCmSubscriptionsPerDmi(subscribersPerDmi)
+ then: 'the mapped values are grouped as expected for dmi-1'
+ assert result['dmi-1'].dmiCmSubscriptionPredicates.size() == 1
+ assert result['dmi-1'].dmiCmSubscriptionPredicates[0].targetCmHandleIds.containsAll(['ch-1', 'ch-2'])
+ and: 'similarly for dmi-2'
+ assert result['dmi-2'].dmiCmSubscriptionPredicates.size() == 2
+ assert result['dmi-2'].dmiCmSubscriptionPredicates[0].targetCmHandleIds.contains('ch-3')
+ assert result['dmi-2'].dmiCmSubscriptionPredicates[1].targetCmHandleIds.contains('ch-3')
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumerSpec.groovy
index 6e28d14810..bcf8780873 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumerSpec.groovy
@@ -103,7 +103,7 @@ class DmiOutEventConsumerSpec extends MessagingBaseSpec {
when: 'the event is consumed'
objectUnderTest.consumeDmiOutEvent(consumerRecord)
then: 'correct number of calls to cache'
- expectedCacheCalls * mockDmiCacheHandler.updateDmiSubscriptionStatusPerDmi('sub-1','test-dmi-plugin-name', subscriptionStatus)
+ expectedCacheCalls * mockDmiCacheHandler.updateDmiSubscriptionStatus('sub-1','test-dmi-plugin-name', subscriptionStatus)
and: 'correct number of calls to persist cache'
expectedPersistenceCalls * mockDmiCacheHandler.persistIntoDatabasePerDmi('sub-1','test-dmi-plugin-name')
and: 'correct number of calls to map the ncmp out event'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImplSpec.groovy
index 3f6556d47e..f902c60482 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImplSpec.groovy
@@ -22,6 +22,7 @@ package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiCmSubscriptionDetailsPerDmiMapper
import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiInEventMapper
import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiInEventProducer
import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails
@@ -30,7 +31,10 @@ import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmSubscriptionPer
import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.NcmpInEvent
import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent
import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.DmiInEvent
+import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
import org.onap.cps.ncmp.utils.TestUtils
+import org.onap.cps.spi.model.DataNode
import org.onap.cps.utils.JsonObjectMapper
import spock.lang.Specification
@@ -45,13 +49,15 @@ class CmSubscriptionHandlerImplSpec extends Specification {
def mockCmSubscriptionComparator = Mock(CmSubscriptionComparator)
def mockNcmpOutEventMapper = Mock(NcmpOutEventMapper)
def mockDmiInEventMapper = Mock(DmiInEventMapper)
+ def dmiCmSubscriptionDetailsPerDmiMapper = new DmiCmSubscriptionDetailsPerDmiMapper()
def mockNcmpOutEventProducer = Mock(NcmpOutEventProducer)
def mockDmiInEventProducer = Mock(DmiInEventProducer)
def mockDmiCacheHandler = Mock(DmiCacheHandler)
+ def mockInventoryPersistence = Mock(InventoryPersistence)
def objectUnderTest = new CmSubscriptionHandlerImpl(mockCmSubscriptionPersistenceService,
- mockCmSubscriptionComparator, mockNcmpOutEventMapper, mockDmiInEventMapper,
- mockNcmpOutEventProducer, mockDmiInEventProducer, mockDmiCacheHandler)
+ mockCmSubscriptionComparator, mockNcmpOutEventMapper, mockDmiInEventMapper, dmiCmSubscriptionDetailsPerDmiMapper,
+ mockNcmpOutEventProducer, mockDmiInEventProducer, mockDmiCacheHandler, mockInventoryPersistence)
def testDmiSubscriptionsPerDmi = ["dmi-1": new DmiCmSubscriptionDetails([], PENDING)]
@@ -97,7 +103,7 @@ class CmSubscriptionHandlerImplSpec extends Specification {
then: 'the subscription cache handler is called once'
1 * mockDmiCacheHandler.add('test-id', _)
and: 'the subscription details are updated in the cache'
- 1 * mockDmiCacheHandler.updateDmiSubscriptionStatusPerDmi('test-id', _, ACCEPTED)
+ 1 * mockDmiCacheHandler.updateDmiSubscriptionStatus('test-id', _, ACCEPTED)
and: 'we schedule to send the response after configured time from the cache'
1 * mockNcmpOutEventProducer.publishNcmpOutEvent('test-id', 'subscriptionCreateResponse', null, true)
}
@@ -123,24 +129,44 @@ class CmSubscriptionHandlerImplSpec extends Specification {
}
def 'Consume valid CmNotificationSubscriptionNcmpInEvent delete message'() {
- given: 'a cmNotificationSubscriptionNcmp in event for delete'
- def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json')
- def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, NcmpInEvent.class)
- and: 'relevant details is extracted from the event'
- def subscriptionId = testEventConsumed.getData().getSubscriptionId()
- def predicates = testEventConsumed.getData().getPredicates()
- and: 'the cache handler returns for relevant subscription id'
- 1 * mockDmiCacheHandler.get('test-id') >> testDmiSubscriptionsPerDmi
- when: 'the valid and unique event is consumed'
- objectUnderTest.processSubscriptionDeleteRequest(subscriptionId, predicates)
- then: 'the subscription cache handler is called once'
- 1 * mockDmiCacheHandler.add('test-id', predicates)
- and: 'the mapper handler to get DMI in event is called once'
- 1 * mockDmiInEventMapper.toDmiInEvent(_)
- and: 'the events handler method to publish DMI event is called correct number of times with the correct parameters'
- testDmiSubscriptionsPerDmi.size() * mockDmiInEventProducer.publishDmiInEvent(
- 'test-id', 'dmi-1', 'subscriptionDeleteRequest', _)
- and: 'we schedule to send the response after configured time from the cache'
- 1 * mockNcmpOutEventProducer.publishNcmpOutEvent('test-id', 'subscriptionDeleteResponse', null, true)
+ given: 'a test subscription id'
+ def subscriptionId = 'test-id'
+ and: 'the persistence service returns datanodes'
+ 1 * mockCmSubscriptionPersistenceService.getAllNodesForSubscriptionId(subscriptionId) >>
+ [new DataNode(xpath: "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-1']/filters/filter[@xpath='x/y']", leaves: ['xpath': 'x/y', 'subscriptionIds': ['test-id']]),
+ new DataNode(xpath: "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-2']/filters/filter[@xpath='y/z']", leaves: ['xpath': 'y/z', 'subscriptionIds': ['test-id']])]
+ and: 'the inventory persistence returns yang model cm handles'
+ 1 * mockInventoryPersistence.getYangModelCmHandle('ch-1') >> new YangModelCmHandle(dmiServiceName: 'dmi-1')
+ 1 * mockInventoryPersistence.getYangModelCmHandle('ch-2') >> new YangModelCmHandle(dmiServiceName: 'dmi-2')
+ when: 'the subscription delete request is processed'
+ objectUnderTest.processSubscriptionDeleteRequest(subscriptionId)
+ then: 'the method to publish a dmi event is called with correct parameters'
+ 1 * mockDmiInEventProducer.publishDmiInEvent(subscriptionId,'dmi-1','subscriptionDeleteRequest',_)
+ 1 * mockDmiInEventProducer.publishDmiInEvent(subscriptionId,'dmi-2','subscriptionDeleteRequest',_)
+ and: 'the method to publish nmcp out event is called with correct parameters'
+ 1 * mockNcmpOutEventProducer.publishNcmpOutEvent(subscriptionId, 'subscriptionDeleteResponse', null, true)
+ }
+
+ def 'Delete a subscriber for fully overlapping subscriptions'() {
+ given: 'a test subscription id'
+ def subscriptionId = 'test-id'
+ and: 'the persistence service returns datanodes with multiple subscribers'
+ 1 * mockCmSubscriptionPersistenceService.getAllNodesForSubscriptionId(subscriptionId) >>
+ [new DataNode(xpath: "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-1']/filters/filter[@xpath='x/y']", leaves: ['xpath': 'x/y', 'subscriptionIds': ['test-id','other-id']]),
+ new DataNode(xpath: "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-2']/filters/filter[@xpath='y/z']", leaves: ['xpath': 'y/z', 'subscriptionIds': ['test-id','other-id']])]
+ and: 'the inventory persistence returns yang model cm handles'
+ 1 * mockInventoryPersistence.getYangModelCmHandle('ch-1') >> new YangModelCmHandle(dmiServiceName: 'dmi-1')
+ 1 * mockInventoryPersistence.getYangModelCmHandle('ch-2') >> new YangModelCmHandle(dmiServiceName: 'dmi-2')
+ and: 'the cache handler returns the relevant maps whenever called'
+ 2 * mockDmiCacheHandler.get(subscriptionId) >> ['dmi-1':[:],'dmi-2':[:]]
+ when: 'the subscription delete request is processed'
+ objectUnderTest.processSubscriptionDeleteRequest(subscriptionId)
+ then: 'the method to publish a dmi event is never called'
+ 0 * mockDmiInEventProducer.publishDmiInEvent(_,_,_,_)
+ and: 'the cache handler is called to remove subscriber from database per dmi'
+ 1 * mockDmiCacheHandler.removeFromDatabase('test-id', 'dmi-1')
+ 1 * mockDmiCacheHandler.removeFromDatabase('test-id', 'dmi-2')
+ and: 'the method to publish nmcp out event is called with correct parameters'
+ 1 * mockNcmpOutEventProducer.publishNcmpOutEvent(subscriptionId, 'subscriptionDeleteResponse', null, false)
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumerSpec.groovy
index 2881737e82..9c24e2b005 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumerSpec.groovy
@@ -100,7 +100,7 @@ class NcmpInEventConsumerSpec extends MessagingBaseSpec {
and: 'the log indicates the task completed successfully'
assert loggingEvent.formattedMessage == 'Subscription delete request for source some-resource with subscription id test-id ...'
and: 'the subscription handler service is called once'
- 1 * mockCmSubscriptionHandler.processSubscriptionDeleteRequest('test-id',_)
+ 1 * mockCmSubscriptionHandler.processSubscriptionDeleteRequest('test-id')
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapperSpec.groovy
index 2251a33466..d3c4026962 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapperSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapperSpec.groovy
@@ -53,9 +53,9 @@ class NcmpOutEventMapperSpec extends Specification {
then: 'event is mapped correctly for the subscription'
result.data.subscriptionId == 'test-subscription'
and: 'the cm handle ids are part of correct list'
- result.data.pendingTargets == ['ch-A']
- result.data.acceptedTargets == ['ch-B']
- result.data.rejectedTargets == ['ch-C']
+ result.data.pendingTargets == ['ch-A'] as Set
+ result.data.acceptedTargets == ['ch-B'] as Set
+ result.data.rejectedTargets == ['ch-C'] as Set
}
def 'Check for Cm Notification Rejected Subscription Outgoing event mapping'() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy
index e03682d8c9..afa2e9874e 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy
@@ -83,5 +83,24 @@ class NcmpOutEventProducerSpec extends Specification {
1 * mockDmiCacheHandler.removeAcceptedAndRejectedDmiSubscriptionEntries(subscriptionId)
}
+ def 'No event published when NCMP out event is null'() {
+ given: 'a cm subscription response for the client'
+ def subscriptionId = 'test-subscription-id-3'
+ def eventType = 'subscriptionCreateResponse'
+ def ncmpOutEvent = null
+ and: 'also we have target topic for publishing to client'
+ objectUnderTest.ncmpOutEventTopic = 'client-test-topic'
+ and: 'a deadline to an event'
+ objectUnderTest.dmiOutEventTimeoutInMs = 1000
+ when: 'the event is scheduled to be published'
+ objectUnderTest.publishNcmpOutEvent(subscriptionId, eventType, ncmpOutEvent, true)
+ then: 'we wait for 10ms and then we receive response from DMI'
+ Thread.sleep(10)
+ and: 'we receive NO response from DMI so we publish the message on demand'
+ objectUnderTest.publishNcmpOutEvent(subscriptionId, eventType, ncmpOutEvent, false)
+ and: 'no event published'
+ 0 * mockEventsPublisher.publishCloudEvent(*_)
+ }
+
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy
index d32d143ade..2b91065592 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy
@@ -142,7 +142,7 @@ class CmSubscriptionPersistenceServiceSpec extends Specification {
'NCMP-Admin',
'cm-data-subscriptions',
parentNodeXpath.formatted(datastoreName, 'ch-1'),
- objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']), _)
+ objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']), _, ContentType.JSON)
where:
scenario | datastoreType || datastoreName
'passthrough_running' | PASSTHROUGH_RUNNING || 'ncmp-datastore:passthrough-running'
@@ -188,4 +188,17 @@ class CmSubscriptionPersistenceServiceSpec extends Specification {
'cm handle in same datastore is NOT used for other subscriptions' | [] || 1
}
+ def 'Get all nodes for subscription id'() {
+ given: 'the query service returns nodes for subscription id'
+ def expectedDataNode = new DataNode(xpath: '/some/xpath')
+ def queryServiceResponse = [expectedDataNode].asCollection()
+ 1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', '//filter/subscriptionIds[text()=\'some-id\']', OMIT_DESCENDANTS) >> queryServiceResponse
+ when: 'retrieving all nodes for subscription id'
+ def result = objectUnderTest.getAllNodesForSubscriptionId('some-id')
+ then: 'the result returns correct number of datanodes'
+ assert result.size() == 1
+ and: 'the attribute of the datanode is as expected'
+ assert result.iterator().next().xpath == expectedDataNode.xpath
+ }
+
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
index 970444f643..fd76abb581 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
@@ -28,15 +28,16 @@ import org.onap.cps.ncmp.api.data.models.DataOperationRequest
import org.onap.cps.ncmp.api.exceptions.DmiClientRequestException
import org.onap.cps.ncmp.config.CpsApplicationContext
import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent
+import org.onap.cps.ncmp.impl.data.policyexecutor.PolicyExecutor
import org.onap.cps.ncmp.impl.dmi.DmiOperationsBaseSpec
import org.onap.cps.ncmp.impl.dmi.DmiProperties
-import org.onap.cps.ncmp.impl.dmi.UrlTemplateParameters
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
@@ -48,6 +49,8 @@ import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_OPERATIONAL
import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_RUNNING
import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE
+import static org.onap.cps.ncmp.api.data.models.OperationType.DELETE
+import static org.onap.cps.ncmp.api.data.models.OperationType.PATCH
import static org.onap.cps.ncmp.api.data.models.OperationType.READ
import static org.onap.cps.ncmp.api.data.models.OperationType.UPDATE
import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATA
@@ -76,6 +79,9 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
@SpringBean
PolicyExecutor policyExecutor = Mock()
+ @SpringBean
+ AlternateIdMatcher alternateIdMatcher = Mock()
+
def 'call get resource data for #expectedDataStore from DMI without topic #scenario.'() {
given: 'a cm handle for #cmHandleId'
mockYangModelCmHandleRetrieval(dmiProperties)
@@ -86,6 +92,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
mockDmiRestClient.asynchronousPostOperationWithJsonData(DATA, expectedUrlTemplateWithVariables, expectedJson, READ, NO_AUTH_HEADER) >> responseFromDmi
when: 'get resource data is invoked'
def cmResourceAddress = new CmResourceAddress(expectedDataStore.datastoreName, cmHandleId, resourceIdentifier)
+ alternateIdMatcher.getCmHandleId(cmHandleId) >> cmHandleId
def result = objectUnderTest.getResourceDataFromDmi(cmResourceAddress, expectedOptions, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER).block()
then: 'the result is the response from the DMI service'
assert result.body == '{some-key:some-value}'
@@ -156,6 +163,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
def 'Write data for pass-through:running datastore in DMI.'() {
given: 'a cm handle for #cmHandleId'
mockYangModelCmHandleRetrieval([yangModelCmHandleProperty])
+ alternateIdMatcher.getCmHandleId(cmHandleId) >> cmHandleId
and: 'a positive response from DMI service when it is called with the expected parameters'
def expectedUrlTemplateParameters = new UrlTemplateParameters('myServiceName/dmi/v1/ch/{cmHandleId}/data/ds/{datastore}?resourceIdentifier={resourceIdentifier}', ['resourceIdentifier': resourceIdentifier, 'datastore': 'ncmp-datastore:passthrough-running', 'cmHandleId': cmHandleId])
def expectedJson = '{"operation":"' + expectedOperationInUrl + '","dataType":"some data type","data":"requestData","cmHandleProperties":{"prop1":"val1"},"moduleSetTag":""}'
@@ -171,6 +179,8 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
operation || expectedOperationInUrl
CREATE || 'create'
UPDATE || 'update'
+ DELETE || 'delete'
+ PATCH || 'patch'
}
def 'State Ready validation'() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandlerSpec.groovy
index 9c696dcc7a..314b76183e 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandlerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandlerSpec.groovy
@@ -21,19 +21,35 @@
package org.onap.cps.ncmp.impl.data
import org.onap.cps.api.CpsDataService
+import org.onap.cps.events.EventsPublisher
import org.onap.cps.ncmp.api.data.models.CmResourceAddress
+import org.onap.cps.ncmp.config.CpsApplicationContext
+import org.onap.cps.ncmp.impl.dmi.DmiProperties
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
import org.onap.cps.spi.model.DataNode
+import org.spockframework.spring.SpringBean
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.context.ApplicationContext
+import org.springframework.test.context.ContextConfiguration
import reactor.core.publisher.Mono
import spock.lang.Specification
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+@SpringBootTest
+@ContextConfiguration(classes = [CpsApplicationContext])
class NcmpCachedResourceRequestHandlerSpec extends Specification {
def cpsDataService = Mock(CpsDataService)
def networkCmProxyQueryService= Mock(NetworkCmProxyQueryService)
+ @SpringBean
+ AlternateIdMatcher alternateIdMatcher = Mock()
+
+ @SpringBean
+ ApplicationContext applicationContext = Mock()
+
def objectUnderTest = new NcmpCachedResourceRequestHandler(cpsDataService, networkCmProxyQueryService)
def 'Execute a request with include descendants = #includeDescendants.'() {
@@ -54,6 +70,7 @@ class NcmpCachedResourceRequestHandlerSpec extends Specification {
def dataNode2 = new DataNode(xpath:'p2')
cpsDataService.getDataNodes('datastore','ch-1','resource',OMIT_DESCENDANTS) >> [dataNode1, dataNode2]
when: 'getting the resource data'
+ alternateIdMatcher.getCmHandleId('ch-1') >> 'ch-1'
def result = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, 'options', 'topic', 'request id', false, 'authorization')
then: 'the result is a "Mono" holding just the first data node'
assert result instanceof Mono
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacadeSpec.groovy
index f4e449904b..5f83ad5f83 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacadeSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacadeSpec.groovy
@@ -26,6 +26,7 @@ package org.onap.cps.ncmp.impl.data
import org.onap.cps.ncmp.api.data.models.CmResourceAddress
import org.onap.cps.ncmp.api.data.models.DataOperationRequest
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
import org.onap.cps.spi.model.DataNode
import reactor.core.publisher.Mono
import spock.lang.Specification
@@ -41,8 +42,9 @@ class NetworkCmProxyFacadeSpec extends Specification {
def mockDmiDataOperations = Mock(DmiDataOperations)
def mockNcmpCachedResourceRequestHandler = Mock(NcmpCachedResourceRequestHandler)
def mockNcmpPassthroughResourceRequestHandler = Mock(NcmpPassthroughResourceRequestHandler)
+ def mockAlternateIdMatcher = Mock(AlternateIdMatcher)
- def objectUnderTest = new NetworkCmProxyFacade(mockNcmpCachedResourceRequestHandler, mockNcmpPassthroughResourceRequestHandler, mockDmiDataOperations)
+ def objectUnderTest = new NetworkCmProxyFacade(mockNcmpCachedResourceRequestHandler, mockNcmpPassthroughResourceRequestHandler, mockDmiDataOperations, mockAlternateIdMatcher)
def NO_TOPIC = null
@@ -87,6 +89,7 @@ class NetworkCmProxyFacadeSpec extends Specification {
given: 'a cm resource address for datastore operational'
def cmResourceAddress = new CmResourceAddress('ncmp-datastore:operational', 'some CM Handle', 'some resource Id')
and: 'get resource data from DMI is called'
+ mockAlternateIdMatcher.getCmHandleId('some CM Handle') >> 'some CM Handle'
mockNcmpCachedResourceRequestHandler.executeRequest(cmResourceAddress, 'options', NO_TOPIC, false, 'authorization') >>
Mono.just('dmi response')
when: 'get resource data operational for the given cm resource address is called'
@@ -103,6 +106,4 @@ class NetworkCmProxyFacadeSpec extends Specification {
then: 'DMI called with correct data'
1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId', UPDATE, '{some-json}', 'application/json', 'authorization')
}
-
-
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy
deleted file mode 100644
index 654206776b..0000000000
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.onap.cps.ncmp.impl.data
-
-import ch.qos.logback.classic.Level
-import ch.qos.logback.classic.Logger
-import ch.qos.logback.classic.spi.ILoggingEvent
-import ch.qos.logback.core.read.ListAppender
-import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.test.context.SpringBootTest
-import org.springframework.test.context.ContextConfiguration
-import spock.lang.Specification
-
-import static org.onap.cps.ncmp.api.data.models.OperationType.PATCH
-
-@SpringBootTest
-@ContextConfiguration(classes = [PolicyExecutor])
-class PolicyExecutorSpec extends Specification {
-
- @Autowired
- PolicyExecutor objectUnderTest
-
- def logAppender = Spy(ListAppender<ILoggingEvent>)
-
- def setup() {
- setupLogger()
- }
-
- def cleanup() {
- ((Logger) LoggerFactory.getLogger(PolicyExecutor)).detachAndStopAllAppenders()
- }
-
- def 'Configuration properties.'() {
- expect: 'properties used from application.yml'
- assert objectUnderTest.enabled
- assert objectUnderTest.serverAddress == 'http://localhost'
- assert objectUnderTest.serverPort == '8785'
- }
-
- def 'Permission check logging.'() {
- when: 'permission is checked for an operation'
- def yangModelCmHandle = new YangModelCmHandle(id:'ch-1', alternateId:'fdn1')
- objectUnderTest.checkPermission(yangModelCmHandle, PATCH, 'my credentials','my resource','my change')
- then: 'correct details are logged '
- assert getLogEntry(0) == 'Policy Executor Enabled'
- assert getLogEntry(3).contains('my credentials')
- assert getLogEntry(4).contains('cm_patch')
- assert getLogEntry(5).contains('fdn1')
- assert getLogEntry(6).contains('ch-1')
- assert getLogEntry(7).contains('my resource')
- assert getLogEntry(8).contains('my change')
- }
-
- def 'Permission check with feature disabled.'() {
- given: 'feature is disabled'
- objectUnderTest.enabled = false
- when: 'permission is checked for an operation'
- objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource','my change')
- then: 'nothing is logged'
- assert logAppender.list.isEmpty()
- }
-
- def setupLogger() {
- def logger = LoggerFactory.getLogger(PolicyExecutor)
- logger.setLevel(Level.DEBUG)
- logger.addAppender(logAppender)
- logAppender.start()
- }
-
- def getLogEntry(index) {
- logAppender.list[index].formattedMessage
- }
-}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy
new file mode 100644
index 0000000000..960e6b32f3
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.data.policyexecutor
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.config.PolicyExecutorHttpClientConfig
+import org.onap.cps.ncmp.impl.policyexecutor.PolicyExecutorWebClientConfiguration
+import org.onap.cps.ncmp.utils.WebClientBuilderTestConfig
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
+import spock.lang.Specification
+
+@SpringBootTest
+@ContextConfiguration(classes = [ObjectMapper, PolicyExecutor, PolicyExecutorWebClientConfiguration, PolicyExecutorHttpClientConfig, WebClientBuilderTestConfig ])
+class PolicyExecutorConfigurationSpec extends Specification {
+
+ @Autowired
+ PolicyExecutor objectUnderTest
+
+ def 'Policy executor configuration properties.'() {
+ expect: 'properties used from application.yml'
+ assert objectUnderTest.enabled
+ assert objectUnderTest.defaultDecision == 'some default decision'
+ assert objectUnderTest.serverAddress == 'http://localhost'
+ assert objectUnderTest.serverPort == '8785'
+ assert objectUnderTest.readTimeoutInSeconds == 35
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy
new file mode 100644
index 0000000000..33dcf5d623
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy
@@ -0,0 +1,234 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.data.policyexecutor
+
+import ch.qos.logback.classic.Level
+import ch.qos.logback.classic.Logger
+import ch.qos.logback.classic.spi.ILoggingEvent
+import ch.qos.logback.core.read.ListAppender
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.api.exceptions.NcmpException
+import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
+import org.slf4j.LoggerFactory
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import org.springframework.web.reactive.function.client.WebClient
+import org.springframework.web.reactive.function.client.WebClientResponseException
+import reactor.core.publisher.Mono
+import spock.lang.Specification
+
+import java.util.concurrent.TimeoutException
+
+import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE
+import static org.onap.cps.ncmp.api.data.models.OperationType.DELETE
+import static org.onap.cps.ncmp.api.data.models.OperationType.PATCH
+import static org.onap.cps.ncmp.api.data.models.OperationType.UPDATE
+
+class PolicyExecutorSpec extends Specification {
+
+ def mockWebClient = Mock(WebClient)
+ def mockRequestBodyUriSpec = Mock(WebClient.RequestBodyUriSpec)
+ def mockResponseSpec = Mock(WebClient.ResponseSpec)
+ def spiedObjectMapper = Spy(ObjectMapper)
+
+ PolicyExecutor objectUnderTest = new PolicyExecutor(mockWebClient, spiedObjectMapper)
+
+ def logAppender = Spy(ListAppender<ILoggingEvent>)
+
+ def someValidJson = '{"Hello":"World"}'
+
+ def setup() {
+ setupLogger()
+ objectUnderTest.enabled = true
+ objectUnderTest.serverAddress = 'some host'
+ objectUnderTest.serverPort = 'some port'
+ mockWebClient.post() >> mockRequestBodyUriSpec
+ mockRequestBodyUriSpec.uri(*_) >> mockRequestBodyUriSpec
+ mockRequestBodyUriSpec.header(*_) >> mockRequestBodyUriSpec
+ mockRequestBodyUriSpec.body(*_) >> mockRequestBodyUriSpec
+ mockRequestBodyUriSpec.retrieve() >> mockResponseSpec
+ }
+
+ def cleanup() {
+ ((Logger) LoggerFactory.getLogger(PolicyExecutor)).detachAndStopAllAppenders()
+ }
+
+ def 'Permission check with "allow" decision.'() {
+ given: 'allow response'
+ mockResponse([decision:'allow'], HttpStatus.OK)
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), operationType, 'my credentials','my resource',someValidJson)
+ then: 'system logs the operation is allowed'
+ assert getLogEntry(2) == 'Operation allowed.'
+ and: 'no exception occurs'
+ noExceptionThrown()
+ where: 'all write operations are tested'
+ operationType << [ CREATE, DELETE, PATCH, UPDATE ]
+ }
+
+ def 'Permission check with "other" decision (not allowed).'() {
+ given: 'other response'
+ mockResponse([decision:'other', decisionId:123, message:'I dont like Mondays' ], HttpStatus.OK)
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource',someValidJson)
+ then: 'Policy Executor exception is thrown'
+ def thrownException = thrown(PolicyExecutorException)
+ assert thrownException.message == 'Operation not allowed. Decision id 123 : other'
+ assert thrownException.details == 'I dont like Mondays'
+ }
+
+ def 'Permission check with non-2xx response and "allow" default decision.'() {
+ given: 'non-2xx http response'
+ mockErrorResponse()
+ and: 'the configured default decision is "allow"'
+ objectUnderTest.defaultDecision = 'allow'
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource',someValidJson)
+ then: 'No exception is thrown'
+ noExceptionThrown()
+ }
+
+ def 'Permission check with non-2xx response and "other" default decision.'() {
+ given: 'non-2xx http response'
+ def webClientException = mockErrorResponse()
+ and: 'the configured default decision is NOT "allow"'
+ objectUnderTest.defaultDecision = 'deny by default'
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials', 'my resource', someValidJson)
+ then: 'Policy Executor exception is thrown'
+ def thrownException = thrown(PolicyExecutorException)
+ assert thrownException.message == 'Operation not allowed. Decision id N/A : deny by default'
+ assert thrownException.details == 'Policy Executor returned HTTP Status code 418. Falling back to configured default decision: deny by default'
+ and: 'the cause is the original web client exception'
+ assert thrownException.cause == webClientException
+ }
+
+ def 'Permission check with invalid response from Policy Executor.'() {
+ given: 'invalid response from Policy executor'
+ mockResponseSpec.toEntity(*_) >> Mono.just(new ResponseEntity<>(null, HttpStatus.OK))
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', someValidJson)
+ then: 'system logs the expected message'
+ assert getLogEntry(1) == 'No valid response body from Policy Executor, ignored'
+ }
+
+ def 'Permission check with timeout exception.'() {
+ given: 'a timeout during the request'
+ def timeoutException = new TimeoutException()
+ mockResponseSpec.toEntity(*_) >> { throw new RuntimeException(timeoutException) }
+ and: 'the configured default decision is NOT "allow"'
+ objectUnderTest.defaultDecision = 'deny by default'
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', someValidJson)
+ then: 'Policy Executor exception is thrown'
+ def thrownException = thrown(PolicyExecutorException)
+ assert thrownException.message == 'Operation not allowed. Decision id N/A : deny by default'
+ assert thrownException.details == 'Policy Executor request timed out. Falling back to configured default decision: deny by default'
+ and: 'the cause is the original time out exception'
+ assert thrownException.cause == timeoutException
+ }
+
+ def 'Permission check with unknown host.'() {
+ given: 'a unknown host exception during the request'
+ def unknownHostException = new UnknownHostException()
+ mockResponseSpec.toEntity(*_) >> { throw new RuntimeException(unknownHostException) }
+ and: 'the configured default decision is NOT "allow"'
+ objectUnderTest.defaultDecision = 'deny by default'
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', someValidJson)
+ then: 'Policy Executor exception is thrown'
+ def thrownException = thrown(PolicyExecutorException)
+ assert thrownException.message == 'Operation not allowed. Decision id N/A : deny by default'
+ assert thrownException.details == 'Cannot connect to Policy Executor (some host:some port). Falling back to configured default decision: deny by default'
+ and: 'the cause is the original unknown host exception'
+ assert thrownException.cause == unknownHostException
+ }
+
+ def 'Permission check with #scenario exception and default decision "allow".'() {
+ given: 'a #scenario exception during the request'
+ mockResponseSpec.toEntity(*_) >> { throw new RuntimeException(cause)}
+ and: 'the configured default decision is "allow"'
+ objectUnderTest.defaultDecision = 'allow'
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', someValidJson)
+ then: 'no exception is thrown'
+ noExceptionThrown()
+ where: 'the following exceptions are thrown during the request'
+ scenario | cause
+ 'timeout' | new TimeoutException()
+ 'unknown host' | new UnknownHostException()
+ }
+
+ def 'Permission check with other runtime exception.'() {
+ given: 'some other runtime exception'
+ def originalException = new RuntimeException()
+ mockResponseSpec.toEntity(*_) >> { throw originalException}
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', someValidJson)
+ then: 'The original exception is thrown'
+ def thrownException = thrown(RuntimeException)
+ assert thrownException == originalException
+ }
+
+ def 'Permission check with an invalid change request json.'() {
+ when: 'permission is checked for an invalid change request'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', 'invalid json string')
+ then: 'an ncmp exception thrown'
+ def ncmpException = thrown(NcmpException)
+ ncmpException.message == 'Cannot convert Change Request data to Object'
+ ncmpException.details.contains('invalid json string')
+ }
+
+ def 'Permission check feature disabled.'() {
+ given: 'feature is disabled'
+ objectUnderTest.enabled = false
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource',someValidJson)
+ then: 'system logs that the feature not enabled'
+ assert getLogEntry(0) == 'Policy Executor Enabled: false'
+ }
+
+ def mockResponse(mockResponseAsMap, httpStatus) {
+ JsonNode jsonNode = spiedObjectMapper.readTree(spiedObjectMapper.writeValueAsString(mockResponseAsMap))
+ def mono = Mono.just(new ResponseEntity<>(jsonNode, httpStatus))
+ mockResponseSpec.toEntity(*_) >> mono
+ }
+
+ def mockErrorResponse() {
+ def webClientResponseException = Mock(WebClientResponseException)
+ webClientResponseException.getStatusCode() >> HttpStatus.I_AM_A_TEAPOT
+ mockResponseSpec.toEntity(*_) >> { throw webClientResponseException }
+ return webClientResponseException
+ }
+
+ def setupLogger() {
+ def logger = LoggerFactory.getLogger(PolicyExecutor)
+ logger.setLevel(Level.TRACE)
+ logger.addAppender(logAppender)
+ logAppender.start()
+ }
+
+ def getLogEntry(index) {
+ logAppender.list[index].formattedMessage
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImplSpec.groovy
new file mode 100644
index 0000000000..74bd0484df
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImplSpec.groovy
@@ -0,0 +1,55 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.datajobs
+
+import org.onap.cps.ncmp.impl.dmi.DmiProperties
+import org.onap.cps.ncmp.impl.dmi.DmiRestClient
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters
+import reactor.core.publisher.Mono
+import spock.lang.Specification
+
+class DataJobResultServiceImplSpec extends Specification {
+
+ def mockDmiRestClient = Mock(DmiRestClient)
+ def mockDmiProperties = Mock(DmiProperties)
+ def objectUnderTest = new DataJobResultServiceImpl(mockDmiRestClient, mockDmiProperties)
+
+ def setup() {
+ mockDmiProperties.dmiBasePath >> 'dmi'
+ }
+
+ def 'Retrieve data job result.'() {
+ given: 'the required parameters for querying'
+ def dmiServiceName = 'some-dmi-service'
+ def dataProducerJobId = 'some-data-producer-job-id'
+ def dataProducerId = 'some-data-producer-id'
+ def authorization = 'my authorization header'
+ def destination = 'some-destination'
+ def urlParams = new UrlTemplateParameters('some-dmi-service/dmi/v1/cmwriteJob/dataProducer/{dataProducerId}/dataProducerJob/{dataProducerJobId}/result?destination={destination}', ['dataProducerJobId':'some-data-producer-job-id', 'dataProducerId':'some-data-producer-id', 'destination': 'some-destination'])
+ and: 'the rest client returns the result for the given parameters'
+ mockDmiRestClient.getDataJobResult(urlParams, authorization) >> Mono.just('some result')
+ when: 'the job status is queried'
+ def result = objectUnderTest.getDataJobResult(authorization, dmiServiceName,dataProducerId, dataProducerJobId, destination)
+ then: 'the result from the rest client is returned'
+ assert result != null
+ assert result == 'some result'
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImplSpec.groovy
index 94c490ab07..4b536b9710 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImplSpec.groovy
@@ -40,6 +40,7 @@ class DataJobServiceImplSpec extends Specification {
def objectUnderTest = new DataJobServiceImpl(mockDmiSubJobRequestHandler, mockWriteRequestExaminer)
def myDataJobMetadata = new DataJobMetadata('', '', '')
+ def authorization = 'my authorization header'
def logger = Spy(ListAppender<ILoggingEvent>)
@@ -54,7 +55,7 @@ class DataJobServiceImplSpec extends Specification {
def 'Read data job request.'() {
when: 'read data job request is processed'
def readOperation = new ReadOperation('', '', '', [], [], '', '', 1)
- objectUnderTest.readDataJob('my-job-id', myDataJobMetadata, new DataJobReadRequest([readOperation]))
+ objectUnderTest.readDataJob(authorization, 'my-job-id', myDataJobMetadata, new DataJobReadRequest([readOperation]))
then: 'the data job id is correctly logged'
def loggingEvent = logger.list[0]
assert loggingEvent.level == Level.INFO
@@ -67,11 +68,11 @@ class DataJobServiceImplSpec extends Specification {
and: 'a map of producer key and dmi 3gpp write operation'
def dmiWriteOperationsPerProducerKey = [:]
when: 'write data job request is processed'
- objectUnderTest.writeDataJob('my-job-id', myDataJobMetadata, dataJobWriteRequest)
+ objectUnderTest.writeDataJob(authorization, 'my-job-id', myDataJobMetadata, dataJobWriteRequest)
then: 'the examiner service is called and a map is returned'
1 * mockWriteRequestExaminer.splitDmiWriteOperationsFromRequest('my-job-id', dataJobWriteRequest) >> dmiWriteOperationsPerProducerKey
and: 'the dmi request handler is called with the result from the examiner'
- 1 * mockDmiSubJobRequestHandler.sendRequestsToDmi('my-job-id', myDataJobMetadata, dmiWriteOperationsPerProducerKey)
+ 1 * mockDmiSubJobRequestHandler.sendRequestsToDmi(authorization, 'my-job-id', myDataJobMetadata, dmiWriteOperationsPerProducerKey)
}
def setupLogger() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImplSpec.groovy
new file mode 100644
index 0000000000..be46d885e0
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImplSpec.groovy
@@ -0,0 +1,53 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.datajobs
+
+import org.onap.cps.ncmp.impl.dmi.DmiProperties
+import org.onap.cps.ncmp.impl.dmi.DmiRestClient
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters
+import reactor.core.publisher.Mono
+import spock.lang.Specification
+
+class DataJobStatusServiceImplSpec extends Specification {
+
+ def mockDmiRestClient = Mock(DmiRestClient)
+ def mockDmiProperties = Mock(DmiProperties)
+ def objectUnderTest = new DataJobStatusServiceImpl(mockDmiRestClient, mockDmiProperties)
+
+ def setup() {
+ mockDmiProperties.dmiBasePath >> 'dmi'
+ }
+
+ def 'Forward a data job status query to DMI.' () {
+ given: 'the required parameters for querying'
+ def dmiServiceName = 'some-dmi-service'
+ def dataProducerId = 'some-data-producer-id'
+ def dataProducerJobId = 'some-data-producer-job-id'
+ def authorization = 'my authorization header'
+ def urlParams = new UrlTemplateParameters('some-dmi-service/dmi/v1/cmwriteJob/dataProducer/{dataProducerId}/dataProducerJob/{dataProducerJobId}/status', ['dataProducerId':'some-data-producer-id', 'dataProducerJobId':'some-data-producer-job-id'])
+ and: 'the rest client returns a status for the given parameters'
+ mockDmiRestClient.getDataJobStatus(urlParams, authorization) >> Mono.just('some status')
+ when: 'the job status is queried'
+ def status = objectUnderTest.getDataJobStatus(authorization, dmiServiceName, dataProducerId, dataProducerJobId)
+ then: 'the status from the rest client is returned'
+ assert status == 'some status'
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy
index e07b978482..041fbd95ee 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy
@@ -19,22 +19,22 @@ class DmiSubJobRequestHandlerSpec extends Specification {
def mockDmiRestClient = Mock(DmiRestClient)
def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
def mockDmiProperties = Mock(DmiProperties)
- def static NO_AUTH = null
def objectUnderTest = new DmiSubJobRequestHandler(mockDmiRestClient, mockDmiProperties, jsonObjectMapper)
def 'Send a sub-job request to the DMI Plugin.'() {
given: 'a data job id, metadata and a map of producer keys and write operations to create a request'
def dataJobId = 'some-job-id'
- def dataJobMetadata = new DataJobMetadata('', '', '')
- def dmiWriteOperation = new DmiWriteOperation('', '', '', null, '', [:])
- def dmiWriteOperationsPerProducerKey = [new ProducerKey('', ''): [dmiWriteOperation]]
- def response = new ResponseEntity<>(new SubJobWriteResponse('my-sub-job-id', '', ''), HttpStatus.OK)
+ def dataJobMetadata = new DataJobMetadata('d1', 't1', 't2')
+ def dmiWriteOperation = new DmiWriteOperation('p', 'operation', 'tag', null, 'o1', [:])
+ def dmiWriteOperationsPerProducerKey = [new ProducerKey('dmi1', 'prod1'): [dmiWriteOperation]]
+ def authorization = 'my authorization header'
+ and: 'the dmi rest client will return a response (for the correct parameters)'
+ def responseEntity = new ResponseEntity<>(new SubJobWriteResponse('my-sub-job-id', 'dmi1', 'prod1'), HttpStatus.OK)
+ def expectedJson = '{"destination":"d1","dataAcceptType":"t1","dataContentType":"t2","dataProducerId":"prod1","dataJobId":"some-job-id","data":[{"path":"p","op":"operation","moduleSetTag":"tag","value":null,"operationId":"o1","privateProperties":{}}]}'
+ mockDmiRestClient.synchronousPostOperationWithJsonData(RequiredDmiService.DATA, _, expectedJson, OperationType.CREATE, authorization) >> responseEntity
when: 'sending request to DMI invoked'
- objectUnderTest.sendRequestsToDmi(dataJobId, dataJobMetadata, dmiWriteOperationsPerProducerKey)
- then: 'the dmi rest client is called'
- 1 * mockDmiRestClient.synchronousPostOperationWithJsonData(RequiredDmiService.DATA, _, _, OperationType.CREATE, NO_AUTH) >> response
- and: 'the result contains the expected sub-job write responses'
- def result = response.body
- assert result.subJobId() == 'my-sub-job-id'
+ objectUnderTest.sendRequestsToDmi(authorization, dataJobId, dataJobMetadata, dmiWriteOperationsPerProducerKey)
+ then: 'the result contains the expected sub-job id'
+ assert responseEntity.body.subJobId == 'my-sub-job-id'
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy
index affbf2aa4f..65fda8718c 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy
@@ -46,6 +46,7 @@ abstract class DmiOperationsBaseSpec extends Specification {
def yangModelCmHandle = new YangModelCmHandle()
def static dmiServiceName = 'myServiceName'
def static cmHandleId = 'some-cm-handle'
+ def static alternateId = 'alt-id-' + cmHandleId
def static resourceIdentifier = 'parent/child'
def mockYangModelCmHandleRetrieval(dmiProperties) {
@@ -68,6 +69,7 @@ abstract class DmiOperationsBaseSpec extends Specification {
yangModelCmHandle.dmiServiceName = dmiServiceName
yangModelCmHandle.dmiProperties = dmiProperties
yangModelCmHandle.id = cmHandleId
+ yangModelCmHandle.alternateId = alternateId
yangModelCmHandle.compositeState = new CompositeState()
yangModelCmHandle.compositeState.cmHandleState = CmHandleState.READY
yangModelCmHandle.moduleSetTag = moduleSetTag
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy
index 3dadf23249..4d47ef14a0 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy
@@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode
import org.onap.cps.ncmp.api.exceptions.DmiClientRequestException
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
import org.springframework.http.HttpHeaders
@@ -163,4 +164,28 @@ class DmiRestClientSpec extends Specification {
'DMI basic auth disabled, with NCMP bearer token' | false | BEARER_AUTH_HEADER || BEARER_AUTH_HEADER
'DMI basic auth disabled, with NCMP basic auth' | false | BASIC_AUTH_HEADER || NO_AUTH_HEADER
}
+
+ def 'DMI GET Operation for DMI Data Service '() {
+ given: 'the Data web client returns a valid response entity for the expected parameters'
+ mockDataServicesWebClient.get() >> mockRequestBody
+ def jsonNode = jsonObjectMapper.convertJsonString('{"status":"some status"}', JsonNode.class)
+ ((ObjectNode) jsonNode).put('status', 'some status')
+ mockResponse.bodyToMono(JsonNode.class) >> Mono.just(jsonNode)
+ when: 'GET operation is invoked for Data Service'
+ def response = objectUnderTest.getDataJobStatus(urlTemplateParameters, NO_AUTH_HEADER).block()
+ then: 'the response equals to the expected value'
+ assert response == 'some status'
+ }
+
+ def 'Get data job result from DMI.'() {
+ given: 'the Data web client returns a valid response entity for the expected parameters'
+ mockDataServicesWebClient.get() >> mockRequestBody
+ def result = 'some result'
+ mockResponse.bodyToMono(String.class) >> Mono.just(result)
+ when: 'GET operation is invoked for Data Service'
+ def response = objectUnderTest.getDataJobResult(urlTemplateParameters, NO_AUTH_HEADER).block()
+ then: 'the response has some value'
+ assert response != null
+ assert result == 'some result'
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiWebClientConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiWebClientConfigurationSpec.groovy
deleted file mode 100644
index fca47d8c24..0000000000
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiWebClientConfigurationSpec.groovy
+++ /dev/null
@@ -1,77 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- * Copyright (C) 2024 Nordix Foundation.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.impl.dmi
-
-
-import org.onap.cps.ncmp.config.HttpClientConfiguration
-import org.springframework.boot.context.properties.EnableConfigurationProperties
-import org.springframework.boot.test.context.SpringBootTest
-import org.springframework.test.context.ContextConfiguration
-import org.springframework.test.context.TestPropertySource
-import org.springframework.web.reactive.function.client.WebClient
-import spock.lang.Specification
-
-@SpringBootTest
-@ContextConfiguration(classes = [HttpClientConfiguration])
-@TestPropertySource(properties = ['ncmp.dmi.httpclient.data-services.connectionTimeoutInSeconds=1', 'ncmp.dmi.httpclient.model-services.maximumInMemorySizeInMegabytes=1'])
-@EnableConfigurationProperties
-class DmiWebClientConfigurationSpec extends Specification {
-
- def webClientBuilder = Mock(WebClient.Builder) {
- defaultHeaders(_) >> it
- clientConnector(_) >> it
- codecs(_) >> it
- build() >> Mock(WebClient)
- }
-
- def httpClientConfiguration = Spy(HttpClientConfiguration.class)
-
- def objectUnderTest = new DmiWebClientConfiguration(httpClientConfiguration)
-
- def 'Web Client Configuration construction.'() {
- expect: 'the system can create an instance'
- new DmiWebClientConfiguration(httpClientConfiguration) != null
- }
-
- def 'Creating a web client instance data service.'() {
- given: 'Web client configuration is invoked'
- def dataServicesWebClient = objectUnderTest.dataServicesWebClient(webClientBuilder)
- expect: 'the system can create an instance for data service'
- assert dataServicesWebClient != null
- assert dataServicesWebClient instanceof WebClient
- }
-
- def 'Creating a web client instance model service.'() {
- given: 'Web client configuration invoked'
- def modelServicesWebClient = objectUnderTest.modelServicesWebClient(webClientBuilder)
- expect: 'the system can create an instance for model service'
- assert modelServicesWebClient != null
- assert modelServicesWebClient instanceof WebClient
- }
-
- def 'Creating a web client instance health service.'() {
- given: 'Web client configuration invoked'
- def healthChecksWebClient = objectUnderTest.healthChecksWebClient(webClientBuilder)
- expect: 'the system can create an instance for health service'
- assert healthChecksWebClient != null
- assert healthChecksWebClient instanceof WebClient
- }
-}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfigurationSpec.groovy
new file mode 100644
index 0000000000..cb209beef0
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfigurationSpec.groovy
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.dmi
+
+
+import org.onap.cps.ncmp.config.DmiHttpClientConfig
+import org.springframework.boot.context.properties.EnableConfigurationProperties
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.web.reactive.function.client.WebClient
+import spock.lang.Specification
+
+@SpringBootTest
+@ContextConfiguration(classes = [DmiHttpClientConfig])
+@EnableConfigurationProperties
+class DmiWebClientsConfigurationSpec extends Specification {
+
+ def webClientBuilder = Mock(WebClient.Builder) {
+ defaultHeaders(_) >> it
+ clientConnector(_) >> it
+ codecs(_) >> it
+ build() >> Mock(WebClient)
+ }
+
+ def dmiHttpClientConfiguration = Spy(DmiHttpClientConfig.class)
+
+ def objectUnderTest = new DmiWebClientsConfiguration(dmiHttpClientConfiguration)
+
+ def 'Web client for data services.'() {
+ when: 'creating a web client for dmi data services'
+ def result = objectUnderTest.dataServicesWebClient(webClientBuilder)
+ then: 'a web client is created successfully'
+ assert result != null
+ assert result instanceof WebClient
+ }
+
+ def 'Web client model services.'() {
+ when: 'creating a web client for dmi model services'
+ def result = objectUnderTest.modelServicesWebClient(webClientBuilder)
+ then: 'a web client is created successfully'
+ assert result != null
+ assert result instanceof WebClient
+ }
+
+ def 'Web client health check services.'() {
+ when: 'creating a web client for dmi health check services'
+ def result = objectUnderTest.healthChecksWebClient(webClientBuilder)
+ then: 'a web client is created successfully'
+ assert result != null
+ assert result instanceof WebClient
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/AlternateIdCheckerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/AlternateIdCheckerSpec.groovy
index b086e58de8..b976ff4284 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/AlternateIdCheckerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/AlternateIdCheckerSpec.groovy
@@ -24,74 +24,41 @@ import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.model.DataNode
-import org.onap.cps.spi.model.DataNodeBuilder
import spock.lang.Specification
class AlternateIdCheckerSpec extends Specification {
def mockInventoryPersistenceService = Mock(InventoryPersistence)
- def someDataNode = new DataNodeBuilder().build()
- def dataNodeFoundException = new DataNodeNotFoundException('', '')
def objectUnderTest = new AlternateIdChecker(mockInventoryPersistenceService)
- def 'Check new cm handle with new alternate id.'() {
- given: 'inventory persistence can not find cm handle id'
- mockInventoryPersistenceService.getYangModelCmHandle('ch 1') >> {throw dataNodeFoundException}
- and: 'inventory persistence can not find alternate id'
- mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId('alternate id') >> {throw dataNodeFoundException}
- expect: 'mapping can be added'
- assert objectUnderTest.canApplyAlternateId('ch 1', 'alternate id')
- }
-
- def 'Check new cm handle with used alternate id.'() {
- given: 'inventory persistence can not find cm handle id'
- mockInventoryPersistenceService.getYangModelCmHandle('ch 1') >> {throw dataNodeFoundException}
- and: 'inventory persistence can find alternate id'
- mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId('alternate id') >> { someDataNode }
- expect: 'mapping can not be added'
- assert objectUnderTest.canApplyAlternateId('ch 1', 'alternate id') == false
- }
-
- def 'Check for existing cm handle with #currentAlternateId.'() {
- given: 'a cm handle with the #currentAlternateId'
- def yangModelCmHandle = new YangModelCmHandle(alternateId: currentAlternateId)
- and: 'inventory service finds the cm handle'
- mockInventoryPersistenceService.getYangModelCmHandle('my cm handle') >> yangModelCmHandle
- expect: 'add mapping returns expected result'
- assert canAdd == objectUnderTest.canApplyAlternateId('my cm handle', 'same alternate id')
- where: 'following alternate ids is used'
- currentAlternateId || canAdd
- 'same alternate id' || true
- 'other alternate id' || false
- }
-
def 'Check a batch of created cm handles with #scenario.'() {
- given: 'a batch of 2 new cm handles alternate id ids #alt1 and #alt2'
+ given: 'a batch of 2 new cm handles with alternate ids #alt1 and #alt2'
def batch = [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: alt1),
new NcmpServiceCmHandle(cmHandleId: 'ch-2', alternateId: alt2)]
and: 'the database already contains cm handle(s) with these alternate ids: #altAlreadyInDb'
- mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId(_) >>
- { args -> altAlreadyInDb.contains(args[0]) ? new DataNode() : throwDataNodeNotFoundException() }
+ mockInventoryPersistenceService.getCmHandleDataNodesByAlternateIds(_ as Collection<String>) >>
+ { args -> args[0].stream().filter(altId -> altAlreadyInDb.contains(altId)).map(altId -> new DataNode(leaves: ["alternate-id": altId])).toList() }
when: 'the batch of new cm handles is checked'
def result = objectUnderTest.getIdsOfCmHandlesWithRejectedAlternateId(batch, AlternateIdChecker.Operation.CREATE)
then: 'the result contains ids of the rejected cm handles'
assert result == expectedRejectedCmHandleIds
where: 'the following alternate ids are used'
- scenario | alt1 | alt2 | altAlreadyInDb || expectedRejectedCmHandleIds
- 'blank alternate ids' | '' | '' | ['dont matter'] || []
- 'null alternate ids' | null | null | ['dont matter'] || []
- 'new alternate ids' | 'fdn1' | 'fdn2' | ['other fdn'] || []
- 'one already used alternate id' | 'fdn1' | 'fdn2' | ['fdn1'] || ['ch-1']
- 'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || ['ch-2']
+ scenario | alt1 | alt2 | altAlreadyInDb || expectedRejectedCmHandleIds
+ 'blank alternate ids' | '' | '' | ['dont matter'] || []
+ 'null alternate ids' | null | null | ['dont matter'] || []
+ 'new alternate ids' | 'fdn1' | 'fdn2' | ['other fdn'] || []
+ 'one already used alternate id' | 'fdn1' | 'fdn2' | ['fdn1'] || ['ch-1']
+ 'two already used alternate ids' | 'fdn1' | 'fdn2' | ['fdn1', 'fdn2'] || ['ch-1', 'ch-2']
+ 'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || ['ch-2']
}
def 'Check a batch of updates to existing cm handles with #scenario.'() {
- given: 'a batch of 1 existing cm handle update alternate id to #proposedAlt'
+ given: 'a batch of 1 existing cm handle to update alternate id to #proposedAlt'
def batch = [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: proposedAlt)]
and: 'the database already contains a cm handle with alternate id: #altAlreadyInDb'
- mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId(_) >>
- { args -> altAlreadyInDb.equals(args[0]) ? new DataNode() : throwDataNodeNotFoundException() }
+ mockInventoryPersistenceService.getCmHandleDataNodesByAlternateIds(_ as Collection<String>) >>
+ { args -> args[0].stream().filter(altId -> altAlreadyInDb == altId).map(altId -> new DataNode(leaves: ["alternate-id": altId])).toList() }
mockInventoryPersistenceService.getYangModelCmHandle(_) >> new YangModelCmHandle(alternateId: altAlreadyInDb)
when: 'the batch of cm handle updates is checked'
def result = objectUnderTest.getIdsOfCmHandlesWithRejectedAlternateId(batch, AlternateIdChecker.Operation.UPDATE)
@@ -104,9 +71,20 @@ class AlternateIdCheckerSpec extends Specification {
'used different alternate id' | 'otherFdn' | 'fdn1' || ['ch-1']
}
- def throwDataNodeNotFoundException() {
- // cannot 'return' an exception in conditional stub behavior, so hence a method call that will always throw this exception
- throw dataNodeFoundException
+ def 'Check update of non-existing cm handle.'() {
+ given: 'a batch of 1 non-existing cm handle to update alternate id'
+ def batch = [new NcmpServiceCmHandle(cmHandleId: 'non-existing', alternateId: 'altId')]
+ and: 'the database does not contain any cm handles'
+ mockInventoryPersistenceService.getCmHandleDataNodesByAlternateIds(_) >> []
+ mockInventoryPersistenceService.getYangModelCmHandle(_) >> { throwDataNodeNotFoundException() }
+ when: 'the batch of cm handle updates is checked'
+ def result = objectUnderTest.getIdsOfCmHandlesWithRejectedAlternateId(batch, AlternateIdChecker.Operation.UPDATE)
+ then: 'the result has no rejected cm handles'
+ assert result.empty
+ }
+
+ static throwDataNodeNotFoundException() {
+ throw new DataNodeNotFoundException('', '')
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy
index 36fd755774..7e34fe2822 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-2024 Nordix Foundation
* Modifications Copyright (C) 2023 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,11 +21,12 @@
package org.onap.cps.ncmp.impl.inventory
+import org.onap.cps.api.CpsDataService
+import org.onap.cps.api.CpsQueryService
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.ncmp.api.inventory.models.TrustLevel
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
-import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.model.DataNode
-import org.onap.cps.spi.utils.CpsValidator
import spock.lang.Shared
import spock.lang.Specification
@@ -37,7 +38,8 @@ import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
class CmHandleQueryServiceImplSpec extends Specification {
- def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
+ def mockCpsQueryService = Mock(CpsQueryService)
+ def mockCpsDataService = Mock(CpsDataService)
def trustLevelPerDmiPlugin = [:]
@@ -45,7 +47,7 @@ class CmHandleQueryServiceImplSpec extends Specification {
def mockCpsValidator = Mock(CpsValidator)
- def objectUnderTest = new CmHandleQueryServiceImpl(mockCpsDataPersistenceService, trustLevelPerDmiPlugin, trustLevelPerCmHandle, mockCpsValidator)
+ def objectUnderTest = new CmHandleQueryServiceImpl(mockCpsDataService, mockCpsQueryService, trustLevelPerDmiPlugin, trustLevelPerCmHandle, mockCpsValidator)
@Shared
def static sampleDataNodes = [new DataNode()]
@@ -104,7 +106,7 @@ class CmHandleQueryServiceImplSpec extends Specification {
def 'Query CmHandles by a private field\'s value.'() {
given: 'a data node exists with a certain additional-property'
- mockCpsDataPersistenceService.queryDataNodes(_, _, dataNodeWithPrivateField, _) >> [pnfDemo5]
+ mockCpsQueryService.queryDataNodes(_, _, dataNodeWithPrivateField, _) >> [pnfDemo5]
when: 'a query on CmHandle private properties is executed using a map'
def result = objectUnderTest.queryCmHandleAdditionalProperties(['Contact3': 'newemailforstore3@bookstore.com'])
then: 'one cm handle is returned'
@@ -115,7 +117,7 @@ class CmHandleQueryServiceImplSpec extends Specification {
given: 'a cm handle state to query'
def cmHandleState = CmHandleState.ADVISED
and: 'the persistence service returns a list of data nodes'
- mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ mockCpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
'//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS) >> sampleDataNodes
when: 'cm handles are fetched by state'
def result = objectUnderTest.queryCmHandlesByState(cmHandleState)
@@ -127,7 +129,7 @@ class CmHandleQueryServiceImplSpec extends Specification {
given: 'a cm handle state to compare'
def cmHandleState = state
and: 'the persistence service returns a list of data nodes'
- mockCpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
NCMP_DMI_REGISTRY_PARENT + '/cm-handles[@id=\'some-cm-handle\']/state',
OMIT_DESCENDANTS) >> [new DataNode(leaves: ['cm-handle-state': 'READY'])]
when: 'cm handles are compared by state'
@@ -144,7 +146,7 @@ class CmHandleQueryServiceImplSpec extends Specification {
given: 'a cm handle state to query'
def cmHandleState = CmHandleState.READY
and: 'cps data service returns a list of data nodes'
- mockCpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
NCMP_DMI_REGISTRY_PARENT + '/cm-handles[@id=\'some-cm-handle\']/state',
OMIT_DESCENDANTS) >> [new DataNode(leaves: ['cm-handle-state': 'READY'])]
when: 'cm handles are fetched by state and id'
@@ -157,7 +159,7 @@ class CmHandleQueryServiceImplSpec extends Specification {
given: 'a cm handle state to query'
def cmHandleState = CmHandleState.READY
and: 'cps data service returns a list of data nodes'
- mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ mockCpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
'//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
when: 'cm handles are fetched by the UNSYNCHRONIZED operational sync state'
def result = objectUnderTest.queryCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED)
@@ -167,10 +169,10 @@ class CmHandleQueryServiceImplSpec extends Specification {
def 'Retrieve cm handle by cps path '() {
given: 'a cm handle state to query based on the cps path'
- def cmHandleDataNode = new DataNode(xpath: 'xpath', leaves: ['cm-handle-state': 'LOCKED'])
- def cpsPath = '//cps-path'
- and: 'cps data service returns a valid data node'
- mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ def cmHandleDataNode = new DataNode(xpath: "/dmi-registry/cm-handles[@id='ch-1']", leaves: ['id': 'ch-1'])
+ def cpsPath = "//state[@cm-handle-state='LOCKED']"
+ and: 'cps data service returns a valid data node for cm handle ancestor'
+ mockCpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
cpsPath + '/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS)
>> Arrays.asList(cmHandleDataNode)
when: 'get cm handles by cps path is invoked'
@@ -179,6 +181,20 @@ class CmHandleQueryServiceImplSpec extends Specification {
assert result.contains(cmHandleDataNode)
}
+ def 'Retrieve cm handle by cps path querying cm handle directly'() {
+ given: 'a cm handle to query based on the cps path'
+ def cmHandleDataNode = new DataNode(xpath: "/dmi-registry/cm-handles[@id='ch-2']", leaves: ['id': 'ch-2'])
+ def cpsPath = "//cm-handles[@alternate-id='1']"
+ and: 'cps data service returns a valid data node'
+ mockCpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ cpsPath, INCLUDE_ALL_DESCENDANTS)
+ >> Arrays.asList(cmHandleDataNode)
+ when: 'get cm handles by cps path is invoked'
+ def result = objectUnderTest.queryCmHandleAncestorsByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS)
+ then: 'the returned result is a list of data nodes returned by cps data service'
+ assert result.contains(cmHandleDataNode)
+ }
+
def 'Get all cm handles by dmi plugin identifier'() {
given: 'the DataNodes queried for a given cpsPath are returned from the persistence service.'
mockResponses()
@@ -191,15 +207,15 @@ class CmHandleQueryServiceImplSpec extends Specification {
}
void mockResponses() {
- mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact\" and @value=\"newemailforstore@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo2, pnfDemo4]
- mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"wont_match\" and @value=\"wont_match\"]/ancestor::cm-handles', _) >> []
- mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"newemailforstore2@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo4]
- mockCpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"\"]/ancestor::cm-handles', _) >> []
- mockCpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"READY\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo3]
- mockCpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"LOCKED\"]/ancestor::cm-handles', _) >> [pnfDemo2, pnfDemo4]
- mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo, pnfDemo2]
- mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-data-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo, pnfDemo4]
- mockCpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-model-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo2, pnfDemo4]
+ mockCpsQueryService.queryDataNodes(_, _, '//public-properties[@name=\"Contact\" and @value=\"newemailforstore@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo2, pnfDemo4]
+ mockCpsQueryService.queryDataNodes(_, _, '//public-properties[@name=\"wont_match\" and @value=\"wont_match\"]/ancestor::cm-handles', _) >> []
+ mockCpsQueryService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"newemailforstore2@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo4]
+ mockCpsQueryService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"\"]/ancestor::cm-handles', _) >> []
+ mockCpsQueryService.queryDataNodes(_, _, '//state[@cm-handle-state=\"READY\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo3]
+ mockCpsQueryService.queryDataNodes(_, _, '//state[@cm-handle-state=\"LOCKED\"]/ancestor::cm-handles', _) >> [pnfDemo2, pnfDemo4]
+ mockCpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo, pnfDemo2]
+ mockCpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-data-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo, pnfDemo4]
+ mockCpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@dmi-model-service-name=\'my-dmi-plugin-identifier\']', OMIT_DESCENDANTS) >> [pnfDemo2, pnfDemo4]
}
def static createDataNode(dataNodeId) {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServiceSpec.groovy
index 0c702abea6..dcff2e9b89 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServiceSpec.groovy
@@ -59,13 +59,12 @@ class CmHandleRegistrationServiceSpec extends Specification {
def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
def mockCpsDataService = Mock(CpsDataService)
def mockModuleSyncStartedOnCmHandles = Mock(IMap<String, Object>)
- def trustLevelPerDmiPlugin = [:]
def mockTrustLevelManager = Mock(TrustLevelManager)
def mockAlternateIdChecker = Mock(AlternateIdChecker)
def objectUnderTest = Spy(new CmHandleRegistrationService(
mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCpsDataService, mockLcmEventsCmHandleStateHandler,
- mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin , mockTrustLevelManager, mockAlternateIdChecker))
+ mockModuleSyncStartedOnCmHandles, mockTrustLevelManager, mockAlternateIdChecker))
def setup() {
// always accept all cm handles
@@ -143,9 +142,6 @@ class CmHandleRegistrationServiceSpec extends Specification {
objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
then: 'create cm handles registration and sync modules is called with the correct plugin information'
1 * objectUnderTest.processCreatedCmHandles(dmiPluginRegistration, _)
- and: 'dmi is added to the dmi trustLevel map'
- assert trustLevelPerDmiPlugin.size() == 1
- assert trustLevelPerDmiPlugin.containsKey(expectedDmiPluginRegisteredName)
where:
scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedDmiPluginRegisteredName
'combined DMI plugin' | 'service1' | '' | '' || 'service1'
@@ -212,7 +208,7 @@ class CmHandleRegistrationServiceSpec extends Specification {
when: 'registration is updated'
objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
then: 'trustLevel is set for the created cm-handle'
- 1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(expectedMapping)
+ 1 * mockTrustLevelManager.registerCmHandles(expectedMapping)
where:
scenario | registrationTrustLevel || expectedMapping
'with trusted cm handle' | TrustLevel.COMPLETE || [ 'ch-1' : TrustLevel.COMPLETE ]
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy
index d58a1dfa06..1830f1331d 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2022-2024 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
- * Modifications Copyright (C) 2023 TechMahindra Ltd.
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.ncmp.api.inventory.models.CompositeState
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
@@ -35,7 +36,7 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.ModuleDefinition
import org.onap.cps.spi.model.ModuleReference
-import org.onap.cps.spi.utils.CpsValidator
+import org.onap.cps.utils.ContentType
import org.onap.cps.utils.JsonObjectMapper
import spock.lang.Shared
import spock.lang.Specification
@@ -166,7 +167,7 @@ class InventoryPersistenceImplSpec extends Specification {
when: 'update cm handle state is invoked with the #scenario state'
objectUnderTest.saveCmHandleState(cmHandleId, compositeState)
then: 'update node leaves is invoked with the correct params'
- 1 * mockCpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']', expectedJsonData, _ as OffsetDateTime)
+ 1 * mockCpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']', expectedJsonData, _ as OffsetDateTime, ContentType.JSON)
where: 'the following states are used'
scenario | cmHandleState || expectedJsonData
'READY' | CmHandleState.READY || '{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
@@ -182,7 +183,7 @@ class InventoryPersistenceImplSpec extends Specification {
def cmHandleStateMap = ['Some-Cm-Handle1' : compositeState1, 'Some-Cm-Handle2' : compositeState2]
objectUnderTest.saveCmHandleStateBatch(cmHandleStateMap)
then: 'update node leaves is invoked with the correct params'
- 1 * mockCpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, _ as OffsetDateTime)
+ 1 * mockCpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, _ as OffsetDateTime, ContentType.JSON)
where: 'the following states are used'
scenario | cmHandleState || cmHandlesJsonDataMap
'READY' | CmHandleState.READY || ['/dmi-registry/cm-handles[@id=\'Some-Cm-Handle1\']':'{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}', '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle2\']':'{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}']
@@ -231,7 +232,7 @@ class InventoryPersistenceImplSpec extends Specification {
objectUnderTest.saveCmHandle(yangModelCmHandle)
then: 'the data service method to save list elements is called once'
1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
- _,null) >> {
+ _,null, ContentType.JSON) >> {
args -> {
assert args[3].startsWith('{"cm-handles":[{"id":"cmhandle","additional-properties":[],"public-properties":[]}]}')
}
@@ -246,7 +247,7 @@ class InventoryPersistenceImplSpec extends Specification {
objectUnderTest.saveCmHandleBatch([yangModelCmHandle1, yangModelCmHandle2])
then: 'CPS Data Service persists both cm handles as a batch'
1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- NCMP_DMI_REGISTRY_PARENT, _,null) >> {
+ NCMP_DMI_REGISTRY_PARENT, _,null, ContentType.JSON) >> {
args -> {
def jsonData = (args[3] as String)
jsonData.contains('cmhandle1')
@@ -296,7 +297,7 @@ class InventoryPersistenceImplSpec extends Specification {
1 * mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, expectedXPath, INCLUDE_ALL_DESCENDANTS)
}
- def 'Get cm handle data node'() {
+ def 'Get cm handle data node by alternate id'() {
given: 'expected xPath to get cmHandle data node'
def expectedXPath = '/dmi-registry/cm-handles[@alternate-id=\'alternate id\']'
and: 'query service is invoked with expected xpath'
@@ -315,6 +316,22 @@ class InventoryPersistenceImplSpec extends Specification {
assert thrownException.getMessage().contains('DataNode not found')
}
+ def 'Get multiple cm handle data nodes by alternate ids'() {
+ given: 'expected xPath to get cmHandle data node'
+ def expectedXPath = "/dmi-registry/cm-handles[@alternate-id='A' or @alternate-id='B']"
+ when: 'getting the cm handle data node'
+ objectUnderTest.getCmHandleDataNodesByAlternateIds(['A', 'B'])
+ then: 'query service is invoked with expected xpath'
+ 1 * mockCmHandleQueries.queryNcmpRegistryByCpsPath(expectedXPath, OMIT_DESCENDANTS)
+ }
+
+ def 'Get multiple cm handle data nodes by alternate ids, passing empty collection'() {
+ when: 'getting the cm handle data node for no alternate ids'
+ objectUnderTest.getCmHandleDataNodesByAlternateIds([])
+ then: 'query service is not invoked'
+ 0 * mockCmHandleQueries.queryNcmpRegistryByCpsPath(_, _)
+ }
+
def 'Get CM handles that has given module names'() {
when: 'the method to get cm handles is called'
objectUnderTest.getCmHandleIdsWithGivenModules(['sample-module-name'])
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy
index 716efd8fdb..fec07556eb 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy
@@ -35,6 +35,8 @@ import org.onap.cps.ncmp.api.inventory.models.TrustLevel
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
+import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
import org.onap.cps.spi.model.ConditionProperties
import org.onap.cps.utils.JsonObjectMapper
import spock.lang.Specification
@@ -46,9 +48,9 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification {
def mockParameterizedCmHandleQueryService = Mock(ParameterizedCmHandleQueryService)
def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
def mockInventoryPersistence = Mock(InventoryPersistence)
- def trustLevelPerCmHandle = [:]
-
- def objectUnderTest = new NetworkCmProxyInventoryFacade(mockCmHandleRegistrationService, mockCmHandleQueryService, mockParameterizedCmHandleQueryService, mockInventoryPersistence, spiedJsonObjectMapper, trustLevelPerCmHandle)
+ def mockTrustLevelManager = Mock(TrustLevelManager)
+ def mockAlternateIdMatcher = Mock(AlternateIdMatcher)
+ def objectUnderTest = new NetworkCmProxyInventoryFacade(mockCmHandleRegistrationService, mockCmHandleQueryService, mockParameterizedCmHandleQueryService, mockInventoryPersistence, spiedJsonObjectMapper, mockTrustLevelManager, mockAlternateIdMatcher)
def 'Update DMI Registration'() {
given: 'an (updated) dmi plugin registration'
@@ -87,14 +89,20 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification {
assert result.containsAll('cm-handle-1','cm-handle-2')
}
- def 'Getting Yang Resources.'() {
+ def 'Getting Yang Resources for a given #scenario'() {
when: 'yang resources is called'
- objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
- then: 'CPS module services is invoked for the correct dataspace and cm handle'
+ objectUnderTest.getYangResourcesModuleReferences(cmHandleRef)
+ then: 'alternate id matcher is called'
+ mockAlternateIdMatcher.getCmHandleId(cmHandleRef) >> 'some-cm-handle'
+ and: 'CPS module services is invoked for the correct cm handle'
1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle')
+ where: 'following cm handle reference is used'
+ scenario | cmHandleRef
+ 'Cm Handle Reference as cm handle-id' | 'some-cm-handle'
+ 'Cm Handle Reference as alternate-id' | 'some-alternate-id'
}
- def 'Get a cm handle.'() {
+ def 'Get a cm handle details using #scenario'() {
given: 'the system returns a yang modelled cm handle'
def dmiServiceName = 'some service name'
def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
@@ -106,17 +114,18 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification {
def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
def moduleSetTag = 'some-module-set-tag'
def alternateId = 'some-alternate-id'
- def yangModelCmHandle = new YangModelCmHandle(id: 'ch-1', dmiServiceName: dmiServiceName, dmiProperties: dmiProperties,
+ def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName, dmiProperties: dmiProperties,
publicProperties: publicProperties, compositeState: compositeState, moduleSetTag: moduleSetTag, alternateId: alternateId)
- 1 * mockInventoryPersistence.getYangModelCmHandle('ch-1') >> yangModelCmHandle
+ mockAlternateIdMatcher.getCmHandleId(cmHandleRef) >> 'some-cm-handle'
+ 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
and: 'a trust level for the cm handle in the cache'
- trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE)
+ mockTrustLevelManager.getEffectiveTrustLevel(*_) >> TrustLevel.COMPLETE
when: 'getting cm handle details for a given cm handle id from ncmp service'
- def result = objectUnderTest.getNcmpServiceCmHandle('ch-1')
+ def result = objectUnderTest.getNcmpServiceCmHandle(cmHandleRef)
then: 'the result is a ncmpServiceCmHandle'
assert result.class == NcmpServiceCmHandle.class
and: 'the cm handle contains the cm handle id'
- assert result.cmHandleId == 'ch-1'
+ assert result.cmHandleId == 'some-cm-handle'
and: 'the cm handle contains the alternate id'
assert result.alternateId == 'some-alternate-id'
and: 'the cm handle contains the module-set-tag'
@@ -129,22 +138,34 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification {
assert result.compositeState == compositeState
and: 'the cm handle contains the trust level from the cache'
assert result.currentTrustLevel == TrustLevel.COMPLETE
+ where: 'following cm handle reference is used'
+ scenario | cmHandleRef
+ 'Cm Handle Reference as cm handle-id' | 'some-cm-handle'
+ 'Cm Handle Reference as alternate-id' | 'some-alternate-id'
}
- def 'Get cm handle public properties'() {
+ def 'Get cm handle public properties using #scenario'() {
given: 'a yang modelled cm handle'
def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
- def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
+ def cmHandleId = 'some-cm-handle'
+ def alternateId = 'some-alternate-id'
+ def yangModelCmHandle = new YangModelCmHandle(id:cmHandleId, alternateId: alternateId, dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
+ and: 'we have corresponding cm handle for the cm handle reference'
+ 1 * mockAlternateIdMatcher.getCmHandleId(cmHandleRef) >> cmHandleId
and: 'the system returns this yang modelled cm handle'
- 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
- when: 'getting cm handle public properties for a given cm handle id from ncmp service'
- def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle')
+ 1 * mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle
+ when: 'getting cm handle public properties for a given cm handle reference from ncmp service'
+ def result = objectUnderTest.getCmHandlePublicProperties(cmHandleRef)
then: 'the result returns the correct data'
assert result == [ 'public prop' : 'some public prop' ]
+ where: 'following cm handle reference is used'
+ scenario | cmHandleRef
+ 'Cm Handle Reference as cm handle-id' | 'some-cm-handle'
+ 'Cm Handle Reference as alternate-id' | 'some-alternate-id'
}
- def 'Get cm handle composite state'() {
+ def 'Get cm handle composite state using #scenario'() {
given: 'a yang modelled cm handle'
def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
@@ -153,13 +174,21 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification {
dataStores: dataStores())
def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
- def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
+ def cmHandleId = 'some-cm-handle'
+ def alternateId = 'some-alternate-id'
+ def yangModelCmHandle = new YangModelCmHandle(id:cmHandleId, alternateId: alternateId, dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
+ and: 'we have corresponding cm handle for the cm handle reference'
+ 1 * mockAlternateIdMatcher.getCmHandleId(cmHandleRef) >> cmHandleId
and: 'the system returns this yang modelled cm handle'
- 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
+ 1 * mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle
when: 'getting cm handle composite state for a given cm handle id from ncmp service'
- def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle')
+ def result = objectUnderTest.getCmHandleCompositeState(cmHandleRef)
then: 'the result returns the correct data'
assert result == compositeState
+ where: 'following cm handle reference is used'
+ scenario | cmHandleRef
+ 'Cm Handle Reference as cm handle-id' | 'some-cm-handle'
+ 'Cm Handle Reference as alternate-id' | 'some-alternate-id'
}
def 'Execute cm handle id search'() {
@@ -179,18 +208,30 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification {
assert result == ['cm-handle-id-1']
}
- def 'Getting module definitions by module'() {
- when: 'get module definitions is performed with module name'
- objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
- then: 'ncmp inventory persistence service is invoked once with correct parameters'
+ def 'Getting module definitions by module for a given #scenario'() {
+ when: 'get module definitions is performed with module name and cm handle reference'
+ objectUnderTest.getModuleDefinitionsByCmHandleAndModule(cmHandleRef, 'some-module', '2021-08-04')
+ then: 'alternate id matcher returns some cm handle id for a given cm handle reference'
+ mockAlternateIdMatcher.getCmHandleId(cmHandleRef) >> 'some-cm-handle'
+ and: 'ncmp inventory persistence service is invoked once with correct parameters'
1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
+ where: 'following cm handle reference is used'
+ scenario | cmHandleRef
+ 'Cm Handle Reference as cm handle-id' | 'some-cm-handle'
+ 'Cm Handle Reference as alternate-id' | 'some-alternate-id'
}
- def 'Getting module definitions by cm handle id'() {
- when: 'get module definitions is performed with cm handle id'
- objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
+ def 'Getting module definitions for a given #scenario'() {
+ when: 'get module definitions is performed with cm handle reference'
+ objectUnderTest.getModuleDefinitionsByCmHandleReference(cmHandleRef)
+ then: 'alternate id matcher returns some cm handle id for a given cm handle reference'
+ mockAlternateIdMatcher.getCmHandleId(cmHandleRef) >> 'some-cm-handle'
then: 'ncmp inventory persistence service is invoked once with correct parameter'
1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
+ where: 'following cm handle reference is used'
+ scenario | cmHandleRef
+ 'Cm Handle Reference as cm handle-id' | 'some-cm-handle'
+ 'Cm Handle Reference as alternate-id' | 'some-alternate-id'
}
def 'Execute cm handle search'() {
@@ -204,16 +245,16 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification {
mockParameterizedCmHandleQueryService.queryCmHandles(
spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
>> [new NcmpServiceCmHandle(cmHandleId: 'ch-0'), new NcmpServiceCmHandle(cmHandleId: 'ch-1')]
- and: ' a trust level for ch-1'
- trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE)
+ and: 'a trust level for cm handles'
+ mockTrustLevelManager.getEffectiveTrustLevel(*_) >> TrustLevel.COMPLETE
when: 'execute cm handle search is called'
def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
then: 'result consists of the two cm handles returned by the CPS Data Service'
assert result.size() == 2
assert result[0].cmHandleId == 'ch-0'
assert result[1].cmHandleId == 'ch-1'
- and: 'only ch-1 has a trust level'
- assert result[0].currentTrustLevel == null
+ and: 'cm handles have trust level'
+ assert result[0].currentTrustLevel == TrustLevel.COMPLETE
assert result[1].currentTrustLevel == TrustLevel.COMPLETE
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperationsSpec.groovy
index 196a1cd360..c80aa7b242 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperationsSpec.groovy
@@ -25,7 +25,7 @@ import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.ncmp.impl.dmi.DmiOperationsBaseSpec
import org.onap.cps.ncmp.impl.dmi.DmiProperties
-import org.onap.cps.ncmp.impl.dmi.UrlTemplateParameters
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy
index babe8101dd..65b7ff6d6f 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy
@@ -40,12 +40,8 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import spock.lang.Specification
-
-import java.time.OffsetDateTime
-import java.time.format.DateTimeFormatter
import java.util.stream.Collectors
-import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.LOCKED_MISBEHAVING
import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_SYNC_FAILED
import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_UPGRADE
import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_UPGRADE_FAILED
@@ -60,17 +56,12 @@ class ModuleOperationsUtilsSpec extends Specification{
def objectUnderTest = new ModuleOperationsUtils(mockCmHandleQueries, mockDmiDataOperations, jsonObjectMapper)
- def static neverUpdatedBefore = '1900-01-01T00:00:00.000+0100'
-
- def static now = OffsetDateTime.now()
-
- def static nowAsString = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(now)
-
def static dataNode = new DataNode(leaves: ['id': 'cm-handle-123'])
def applicationContext = new AnnotationConfigApplicationContext()
def logger = (Logger) LoggerFactory.getLogger(ModuleOperationsUtils)
+
def loggingListAppender
void setup() {
@@ -103,7 +94,7 @@ class ModuleOperationsUtilsSpec extends Specification{
given: 'A locked state'
def compositeState = new CompositeState(lockReason: lockReason)
when: 'update cm handle details and attempts is called'
- objectUnderTest.updateLockReasonDetailsAndAttempts(compositeState, MODULE_SYNC_FAILED, 'new error message')
+ objectUnderTest.updateLockReasonWithAttempts(compositeState, MODULE_SYNC_FAILED, 'new error message')
then: 'the composite state lock reason and details are updated'
assert compositeState.lockReason.lockReasonCategory == MODULE_SYNC_FAILED
assert compositeState.lockReason.details.contains(expectedDetails)
@@ -118,14 +109,14 @@ class ModuleOperationsUtilsSpec extends Specification{
def compositeState = new CompositeStateBuilder().withCmHandleState(CmHandleState.LOCKED)
.withLockReason(MODULE_UPGRADE, "Upgrade to ModuleSetTag: " + moduleSetTag).build()
when: 'update cm handle details'
- objectUnderTest.updateLockReasonDetailsAndAttempts(compositeState, MODULE_UPGRADE_FAILED, 'new error message')
+ objectUnderTest.updateLockReasonWithAttempts(compositeState, MODULE_UPGRADE_FAILED, 'new error message')
then: 'the composite state lock reason and details are updated'
assert compositeState.lockReason.lockReasonCategory == MODULE_UPGRADE_FAILED
- assert compositeState.lockReason.details.contains("Upgrade to ModuleSetTag: " + expectedDetails)
+ assert compositeState.lockReason.details.contains(expectedDetails)
where:
scenario | moduleSetTag || expectedDetails
- 'a module set tag' | 'someModuleSetTag' || 'someModuleSetTag'
- 'empty module set tag' | '' || ''
+ 'a module set tag' | 'someModuleSetTag' || 'Upgrade to ModuleSetTag: someModuleSetTag'
+ 'empty module set tag' | '' || 'Attempt'
}
def 'Get all locked cm-Handles where lock reasons are model sync failed or upgrade'() {
@@ -135,49 +126,9 @@ class ModuleOperationsUtilsSpec extends Specification{
when: 'get locked Misbehaving cm handle is called'
def result = objectUnderTest.getCmHandlesThatFailedModelSyncOrUpgrade()
then: 'the returned cm handle collection is the correct size'
- result.size() == 1
+ assert result.size() == 1
and: 'the correct cm handle is returned'
- result[0].id == 'cm-handle-123'
- }
-
- def 'Retry Locked Cm-Handle where the last update time is #scenario'() {
- given: 'Last update was #lastUpdateMinutesAgo minutes ago (-1 means never)'
- def lastUpdatedTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(now.minusMinutes(lastUpdateMinutesAgo))
- if (lastUpdateMinutesAgo < 0 ) {
- lastUpdatedTime = neverUpdatedBefore
- }
- when: 'checking to see if cm handle is ready for retry'
- def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder()
- .withLockReason(MODULE_SYNC_FAILED, lockDetails)
- .withLastUpdatedTime(lastUpdatedTime).build())
- then: 'retry is only attempted when expected'
- assert result == retryExpected
- and: 'logs contain related information'
- def logs = loggingListAppender.list.toString()
- assert logs.contains(logReason)
- where: 'the following parameters are used'
- scenario | lastUpdateMinutesAgo | lockDetails | logReason || retryExpected
- 'never attempted before' | -1 | 'Fist attempt:' | 'First Attempt:' || true
- '1st attempt, last attempt > 2 minute ago' | 3 | 'Attempt #1 failed: some error' | 'Retry due now' || true
- '2nd attempt, last attempt < 4 minutes ago' | 1 | 'Attempt #2 failed: some error' | 'Time until next attempt is 3 minutes:' || false
- '2nd attempt, last attempt > 4 minutes ago' | 5 | 'Attempt #2 failed: some error' | 'Retry due now' || true
- }
-
- def 'Retry Locked Cm-Handle with lock reasons (category) #lockReasonCategory'() {
- when: 'checking to see if cm handle is ready for retry'
- def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder()
- .withLockReason(lockReasonCategory, 'some details')
- .withLastUpdatedTime(nowAsString).build())
- then: 'verify retry attempts'
- assert !result
- and: 'logs contain related information'
- def logs = loggingListAppender.list.toString()
- assert logs.contains(logReason)
- where: 'the following lock reasons occurred'
- scenario | lockReasonCategory || logReason
- 'module upgrade' | MODULE_UPGRADE_FAILED || 'First Attempt:'
- 'module sync failed' | MODULE_SYNC_FAILED || 'First Attempt:'
- 'lock misbehaving' | LOCKED_MISBEHAVING || 'Locked for other reason'
+ assert result[0].id == 'cm-handle-123'
}
def 'Get a Cm-Handle where #scenario'() {
@@ -197,19 +148,25 @@ class ModuleOperationsUtilsSpec extends Specification{
'all Cm-Handle synchronized' | [] | false || 0 | [] as Set
}
- def 'Get resource data through DMI Operations #scenario'() {
- given: 'the inventory persistence service returns a collection of data nodes'
+ def 'Retrieve resource data from DMI operations for #scenario'() {
+ given: 'a JSON string representing the resource data'
def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
- JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString);
- def responseEntity = new ResponseEntity<>(jsonNode, HttpStatus.OK)
+ JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString)
+ and: 'DMI operations are mocked to return a response based on the scenario'
+ def responseEntity = new ResponseEntity<>(statusCode == HttpStatus.OK ? jsonNode : null, statusCode)
mockDmiDataOperations.getAllResourceDataFromDmi('cm-handle-123', _) >> responseEntity
when: 'get resource data is called'
def result = objectUnderTest.getResourceData('cm-handle-123')
- then: 'the returned data is correct'
- result == jsonString
+ then: 'the returned data matches the expected result'
+ assert result == expectedResult
+ where:
+ scenario | statusCode | expectedResult
+ 'successful response' | HttpStatus.OK | '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
+ 'response with not found status' | HttpStatus.NOT_FOUND | null
+ 'response with internal server error' | HttpStatus.INTERNAL_SERVER_ERROR | null
}
- def 'Extract module set tag and number of attempt when lock reason contains #scenario'() {
+ def 'Extract module set tag and number of attempt when lock reason contains #scenario'() {
expect: 'lock reason details are extracted correctly'
def result = objectUnderTest.getLockedCompositeStateDetails(new CompositeStateBuilder().withLockReason(MODULE_UPGRADE, lockReasonDetails).build().lockReason)
and: 'the result contains the correct moduleSetTag'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy
index c3a01a7393..6030e5debf 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy
@@ -30,8 +30,6 @@ import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
import org.onap.cps.spi.CascadeDeleteAllowed
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
-import org.onap.cps.spi.model.DataNode
-import org.onap.cps.spi.model.DataNodeBuilder
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.utils.JsonObjectMapper
import spock.lang.Specification
@@ -49,13 +47,9 @@ class ModuleSyncServiceSpec extends Specification {
def mockJsonObjectMapper = Mock(JsonObjectMapper)
def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService,
- mockCmHandleQueries, mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper)
+ mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper)
def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
- def static cmHandleWithModuleSetTag = new DataNodeBuilder()
- .withXpath("/dmi-registry/cm-handles[@id='otherId']")
- .withLeaves(['id': 'otherId', 'module-set-tag': 'tag-1'])
- .withAnchor('otherId').build()
def 'Sync model for a NEW cm handle using module set tags: #scenario.'() {
given: 'a cm handle state to be synced'
@@ -70,8 +64,8 @@ class ModuleSyncServiceSpec extends Specification {
mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, identifiedNewModuleReferences) >> newModuleNameContentToMap
and: 'the module service identifies #identifiedNewModuleReferences.size() new modules'
mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> identifiedNewModuleReferences
- and: 'system contains other cm handle with "same tag" (that is READY)'
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag
+ and: 'the service returns a list of module references when queried with the specified attributes'
+ mockCpsModuleService.getModuleReferencesByAttribute(*_) >> existingModuleReferences
when: 'module sync is triggered'
objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle)
then: 'create schema set from module is invoked with correct parameters'
@@ -79,10 +73,10 @@ class ModuleSyncServiceSpec extends Specification {
and: 'anchor is created with the correct parameters'
1 * mockCpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', 'ch-1')
where: 'the following parameters are used'
- scenario | existingModuleResourcesInCps | identifiedNewModuleReferences | newModuleNameContentToMap | moduleSetTag | existingCmHandlesWithSameTag
- 'one new module, new tag' | [['module2': '2'], ['module3': '3']] | [new ModuleReference('module1', '1')] | [module1: 'some yang source'] | '' | []
- 'no new module, new tag' | [['module1': '1'], ['module2': '2']] | [] | [:] | 'new-tag-1' | []
- 'same tag' | [['module1': '1'], ['module2': '2']] | [] | [:] | 'same-tag' | [cmHandleWithModuleSetTag]
+ scenario | identifiedNewModuleReferences | newModuleNameContentToMap | moduleSetTag | existingModuleReferences
+ 'one new module, new tag' | [new ModuleReference('module1', '1')] | [module1: 'some yang source'] | '' | []
+ 'no new module, new tag' | [] | [:] | 'new-tag-1' | []
+ 'same tag' | [] | [:] | 'same-tag' | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
}
def 'Upgrade model for an existing cm handle with Module Set Tag where the modules are #scenario'() {
@@ -101,8 +95,8 @@ class ModuleSyncServiceSpec extends Specification {
mockCpsModuleService.identifyNewModuleReferences(_) >> []
and: 'CPS-Core returns list of existing module resources for TBD'
mockCpsModuleService.getYangResourcesModuleReferences(*_) >> [ new ModuleReference('module1','1') ]
- and: 'system contains #existingCmHandlesWithSameTag.size() cm handles with same tag'
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag
+ and: 'the service returns a list of module references when queried with the specified attributes'
+ mockCpsModuleService.getModuleReferencesByAttribute(*_) >> existingModuleReferences
and: 'the other cm handle is a state ready'
mockCmHandleQueries.cmHandleHasState('otherId', CmHandleState.READY) >> true
when: 'module sync is triggered'
@@ -114,9 +108,9 @@ class ModuleSyncServiceSpec extends Specification {
and: 'No anchor is created for the upgraded cm handle'
0 * mockCpsAnchorService.createAnchor(*_)
where: 'the following parameters are used'
- scenario | existingCmHandlesWithSameTag
+ scenario | existingModuleReferences
'new' | []
- 'in database' | [cmHandleWithModuleSetTag]
+ 'in database' | [new ModuleReference('module1', '1')]
}
def 'upgrade model for a existing cm handle'() {
@@ -130,9 +124,8 @@ class ModuleSyncServiceSpec extends Specification {
and: 'the module service returns some module references'
def moduleReferences = [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
mockCpsModuleService.getYangResourcesModuleReferences(*_)>> moduleReferences
- and: 'a cm handle with the same moduleSetTag can be found in the registry'
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1'],
- childDataNodes: [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']/state', leaves: ['cm-handle-state': 'READY'])])]
+ and: 'the service returns a list of module references when queried with the specified attributes'
+ mockCpsModuleService.getModuleReferencesByAttribute(*_) >> moduleReferences
when: 'module upgrade is triggered'
objectUnderTest.syncAndUpgradeSchemaSet(yangModelCmHandle)
then: 'the upgrade is delegated to the module service (with the correct parameters)'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasksSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasksSpec.groovy
index ee49f2f901..4d715d28c9 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasksSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasksSpec.groovy
@@ -32,16 +32,15 @@ import org.onap.cps.ncmp.api.inventory.models.CompositeState
import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
-import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventsCmHandleStateHandler
import org.onap.cps.spi.model.DataNode
import org.slf4j.LoggerFactory
import spock.lang.Specification
-
import java.util.concurrent.atomic.AtomicInteger
import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_SYNC_FAILED
+import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_UPGRADE
import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_UPGRADE_FAILED
class ModuleSyncTasksSpec extends Specification {
@@ -96,70 +95,52 @@ class ModuleSyncTasksSpec extends Specification {
assert batchCount.get() == 4
}
- def 'Module Sync ADVISED cm handle with failure during sync.'() {
- given: 'a cm handle in an ADVISED state'
- def cmHandle = cmHandleAsDataNodeByIdAndState('cm-handle', CmHandleState.ADVISED)
- and: 'the inventory persistence cm handle returns a ADVISED state for the cm handle'
- def cmHandleState = new CompositeState(cmHandleState: CmHandleState.ADVISED)
- 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> cmHandleState
- and: 'module sync service attempts to sync the cm handle and throws an exception'
- 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') }
+ def 'Handle CM handle failure during #scenario and log MODULE_UPGRADE lock reason'() {
+ given: 'a CM handle in LOCKED state with a specific lock reason'
+ def cmHandle = cmHandleAsDataNodeByIdAndState('cm-handle', CmHandleState.LOCKED)
+ def expectedCmHandleState = new CompositeState(cmHandleState: CmHandleState.LOCKED, lockReason: CompositeState
+ .LockReason.builder().lockReasonCategory(lockReasonCategory).details(lockReasonDetails).build())
+ 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> expectedCmHandleState
+ and: 'module sync service attempts to sync/upgrade the CM handle and throws an exception'
+ mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { throw new Exception('some exception') }
+ mockModuleSyncService.syncAndUpgradeSchemaSet(_) >> { throw new Exception('some exception') }
when: 'module sync is executed'
objectUnderTest.performModuleSync([cmHandle], batchCount)
- then: 'update lock reason, details and attempts is invoked'
- 1 * mockSyncUtils.updateLockReasonDetailsAndAttempts(cmHandleState, MODULE_SYNC_FAILED, 'some exception')
+ then: 'lock reason is updated with number of attempts'
+ 1 * mockSyncUtils.updateLockReasonWithAttempts(expectedCmHandleState, expectedLockReasonCategory, 'some exception')
and: 'the state handler is called to update the state to LOCKED'
1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args ->
assertBatch(args, ['cm-handle'], CmHandleState.LOCKED)
}
and: 'batch count is decremented by one'
assert batchCount.get() == 4
- }
-
- def 'Failed cm handle during #scenario.'() {
- given: 'a cm handle in LOCKED state'
- def cmHandle = cmHandleAsDataNodeByIdAndState('cm-handle', CmHandleState.LOCKED)
- and: 'the inventory persistence cm handle returns a LOCKED state with reason for the cm handle'
- def expectedCmHandleState = new CompositeState(cmHandleState: cmHandleState, lockReason: CompositeState
- .LockReason.builder().lockReasonCategory(lockReasonCategory).details(lockReasonDetails).build())
- 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> expectedCmHandleState
- and: 'module sync service attempts to sync/upgrade the cm handle and throws an exception'
- mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') }
- mockModuleSyncService.syncAndUpgradeSchemaSet(*_) >> { throw new Exception('some exception') }
- when: 'module sync is executed'
- objectUnderTest.performModuleSync([cmHandle], batchCount)
- then: 'update lock reason, details and attempts is invoked'
- 1 * mockSyncUtils.updateLockReasonDetailsAndAttempts(expectedCmHandleState, expectedLockReasonCategory, 'some exception')
where:
- scenario | cmHandleState | lockReasonCategory | lockReasonDetails || expectedLockReasonCategory
- 'module upgrade' | CmHandleState.LOCKED | MODULE_UPGRADE_FAILED | 'Upgrade to ModuleSetTag: some-module-set-tag' || MODULE_UPGRADE_FAILED
- 'module sync' | CmHandleState.LOCKED | MODULE_SYNC_FAILED | 'some lock details' || MODULE_SYNC_FAILED
+ scenario | lockReasonCategory | lockReasonDetails || expectedLockReasonCategory
+ 'module sync' | MODULE_SYNC_FAILED | 'some lock details' || MODULE_SYNC_FAILED
+ 'module upgrade' | MODULE_UPGRADE_FAILED | 'Upgrade to ModuleSetTag: some-module-set-tag' || MODULE_UPGRADE_FAILED
+ 'module upgrade' | MODULE_UPGRADE | 'Upgrade in progress' || MODULE_UPGRADE_FAILED
}
+
def 'Reset failed CM Handles #scenario.'() {
given: 'cm handles in an locked state'
def lockedState = new CompositeStateBuilder().withCmHandleState(CmHandleState.LOCKED)
- .withLockReason(LockReasonCategory.MODULE_SYNC_FAILED, '').withLastUpdatedTimeNow().build()
+ .withLockReason(MODULE_SYNC_FAILED, '').withLastUpdatedTimeNow().build()
def yangModelCmHandle1 = new YangModelCmHandle(id: 'cm-handle-1', compositeState: lockedState)
def yangModelCmHandle2 = new YangModelCmHandle(id: 'cm-handle-2', compositeState: lockedState)
- def expectedCmHandleStatePerCmHandle = [(yangModelCmHandle1): CmHandleState.ADVISED]
+ def expectedCmHandleStatePerCmHandle
+ = [(yangModelCmHandle1): CmHandleState.ADVISED, (yangModelCmHandle2): CmHandleState.ADVISED]
and: 'clear in progress map'
resetModuleSyncStartedOnCmHandles(moduleSyncStartedOnCmHandles)
and: 'add cm handle entry into progress map'
moduleSyncStartedOnCmHandles.put('cm-handle-1', 'started')
moduleSyncStartedOnCmHandles.put('cm-handle-2', 'started')
- and: 'sync utils retry locked cm handle returns #isReadyForRetry'
- mockSyncUtils.needsModuleSyncRetryOrUpgrade(lockedState) >>> isReadyForRetry
when: 'resetting failed cm handles'
- objectUnderTest.resetFailedCmHandles([yangModelCmHandle1, yangModelCmHandle2])
+ objectUnderTest.setCmHandlesToAdvised([yangModelCmHandle1, yangModelCmHandle2])
then: 'updated to state "ADVISED" from "READY" is called as often as there are cm handles ready for retry'
- expectedNumberOfInvocationsToUpdateCmHandleState * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(expectedCmHandleStatePerCmHandle)
- and: 'after reset performed size of in progress map'
- assert moduleSyncStartedOnCmHandles.size() == inProgressMapSize
- where:
- scenario | isReadyForRetry | inProgressMapSize || expectedNumberOfInvocationsToUpdateCmHandleState
- 'retry locked cm handle' | [true, false] | 1 || 1
- 'do not retry locked cm handle' | [false, false] | 2 || 0
+ 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(expectedCmHandleStatePerCmHandle)
+ and: 'after reset performed progress map is empty'
+ assert moduleSyncStartedOnCmHandles.size() == 0
}
def 'Module Sync ADVISED cm handle without entry in progress map.'() {
@@ -189,6 +170,24 @@ class ModuleSyncTasksSpec extends Specification {
assert loggingEvent.formattedMessage == 'ch-1 removed from in progress map'
}
+ def 'Sync and upgrade CM handle if in upgrade state for #scenario'() {
+ given: 'a CM handle in an upgrade state'
+ def cmHandle = cmHandleAsDataNodeByIdAndState('cm-handle', CmHandleState.LOCKED)
+ def compositeState = new CompositeState(lockReason: CompositeState.LockReason.builder().lockReasonCategory(lockReasonCategory).build())
+ 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> compositeState
+ when: 'module sync is executed'
+ objectUnderTest.performModuleSync([cmHandle], batchCount)
+ then: 'the module sync service should attempt to sync and upgrade the CM handle'
+ 1 * mockModuleSyncService.syncAndUpgradeSchemaSet(_) >> { args ->
+ assert args[0].id == 'cm-handle'
+ }
+ where: 'the following lock reasons are used'
+ scenario | lockReasonCategory
+ 'module upgrade' | MODULE_UPGRADE
+ 'module upgrade failed' | MODULE_UPGRADE_FAILED
+ }
+
+
def 'Remove non-existing cm handle id from hazelcast map'() {
given: 'hazelcast map does not contains cm handle id'
def result = moduleSyncStartedOnCmHandles.get('non-existing-cm-handle')
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdogSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdogSpec.groovy
index 155edc8bc6..3064a78ff9 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdogSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdogSpec.groovy
@@ -27,6 +27,7 @@ import org.onap.cps.spi.model.DataNode
import spock.lang.Specification
import java.util.concurrent.ArrayBlockingQueue
+import java.util.concurrent.locks.Lock
class ModuleSyncWatchdogSpec extends Specification {
@@ -42,10 +43,13 @@ class ModuleSyncWatchdogSpec extends Specification {
def spiedAsyncTaskExecutor = Spy(AsyncTaskExecutor)
- def objectUnderTest = new ModuleSyncWatchdog(mockSyncUtils, moduleSyncWorkQueue , mockModuleSyncStartedOnCmHandles, mockModuleSyncTasks, spiedAsyncTaskExecutor)
+ def mockWorkQueueLock = Mock(Lock)
+
+ def objectUnderTest = new ModuleSyncWatchdog(mockSyncUtils, moduleSyncWorkQueue , mockModuleSyncStartedOnCmHandles, mockModuleSyncTasks, spiedAsyncTaskExecutor, mockWorkQueueLock)
void setup() {
spiedAsyncTaskExecutor.setupThreadPool()
+ mockWorkQueueLock.tryLock() >> true
}
def 'Module sync advised cm handles with #scenario.'() {
@@ -108,9 +112,9 @@ class ModuleSyncWatchdogSpec extends Specification {
def failedCmHandles = [new YangModelCmHandle()]
mockSyncUtils.getCmHandlesThatFailedModelSyncOrUpgrade() >> failedCmHandles
when: 'reset failed cm handles is started'
- objectUnderTest.resetPreviouslyFailedCmHandles()
+ objectUnderTest.setPreviouslyLockedCmHandlesToAdvised()
then: 'it is delegated to the module sync task (service)'
- 1 * mockModuleSyncTasks.resetFailedCmHandles(failedCmHandles)
+ 1 * mockModuleSyncTasks.setCmHandlesToAdvised(failedCmHandles)
}
def createDataNodes(numberOfDataNodes) {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfigSpec.groovy
index 8e59922c41..4c96d6b822 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfigSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfigSpec.groovy
@@ -46,6 +46,10 @@ class SynchronizationCacheConfigSpec extends Specification {
@Autowired
private IMap<String, Boolean> dataSyncSemaphores
+ def cleanupSpec() {
+ Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').shutdown()
+ }
+
def 'Embedded (hazelcast) Caches for Module and Data Sync.'() {
expect: 'system is able to create an instance of the Module Sync Work Queue'
assert null != moduleSyncWorkQueue
@@ -53,22 +57,19 @@ 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 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')
+ assert Hazelcast.allHazelcastInstances.name.contains('cps-and-ncmp-hazelcast-instance-test-config')
}
def 'Verify configs for Distributed objects'(){
- given: 'the Module Sync Work Queue config'
- def moduleSyncWorkQueueConfig = Hazelcast.getHazelcastInstanceByName('moduleSyncWorkQueue').config
- def moduleSyncDefaultWorkQueueConfig = moduleSyncWorkQueueConfig.queueConfigs.get('defaultQueueConfig')
+ given: 'hazelcast common config'
+ def hzConfig = Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').config
+ and: 'the Module Sync Work Queue config'
+ def moduleSyncDefaultWorkQueueConfig = hzConfig.queueConfigs.get('defaultQueueConfig')
and: 'the Module Sync Started Cm Handle Map config'
- def moduleSyncStartedOnCmHandlesConfig = Hazelcast.getHazelcastInstanceByName('moduleSyncStartedOnCmHandles').config
- def moduleSyncStartedOnCmHandlesMapConfig = moduleSyncStartedOnCmHandlesConfig.mapConfigs.get('moduleSyncStartedConfig')
+ def moduleSyncStartedOnCmHandlesMapConfig = hzConfig.mapConfigs.get('moduleSyncStartedConfig')
and: 'the Data Sync Semaphores Map config'
- def dataSyncSemaphoresConfig = Hazelcast.getHazelcastInstanceByName('dataSyncSemaphores').config
- def dataSyncSemaphoresMapConfig = dataSyncSemaphoresConfig.mapConfigs.get('dataSyncSemaphoresConfig')
+ def dataSyncSemaphoresMapConfig = hzConfig.mapConfigs.get('dataSyncSemaphoresConfig')
expect: 'system created instance with correct config of Module Sync Work Queue'
assert moduleSyncDefaultWorkQueueConfig.backupCount == 1
assert moduleSyncDefaultWorkQueueConfig.asyncBackupCount == 0
@@ -79,28 +80,15 @@ class SynchronizationCacheConfigSpec extends Specification {
assert dataSyncSemaphoresMapConfig.backupCount == 1
assert dataSyncSemaphoresMapConfig.asyncBackupCount == 0
and: 'all instances are part of same cluster'
- def testClusterName = 'cps-and-ncmp-test-caches'
- assert moduleSyncWorkQueueConfig.clusterName == testClusterName
- assert moduleSyncStartedOnCmHandlesConfig.clusterName == testClusterName
- assert dataSyncSemaphoresConfig.clusterName == testClusterName
+ assert hzConfig.clusterName == 'cps-and-ncmp-test-caches'
}
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
+ given: 'common hazelcast network config'
+ def hzConfig = Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').config.networkConfig
+ and: 'all configs has the correct settings'
+ assert hzConfig.join.autoDetectionConfig.enabled
+ assert !hzConfig.join.kubernetesConfig.enabled
}
def 'Verify network config'() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsServiceSpec.groovy
index b745734f0b..73c66089a3 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsServiceSpec.groovy
@@ -20,9 +20,16 @@
package org.onap.cps.ncmp.impl.inventory.sync.lcm
+import static org.onap.cps.ncmp.events.lcm.v1.Values.CmHandleState.ADVISED
+import static org.onap.cps.ncmp.events.lcm.v1.Values.CmHandleState.READY
+
+import io.micrometer.core.instrument.Tag
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry
import org.onap.cps.events.EventsPublisher
+import org.onap.cps.ncmp.events.lcm.v1.Event
import org.onap.cps.ncmp.events.lcm.v1.LcmEvent
import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader
+import org.onap.cps.ncmp.events.lcm.v1.Values
import org.onap.cps.utils.JsonObjectMapper
import org.springframework.kafka.KafkaException
import spock.lang.Specification
@@ -31,14 +38,16 @@ class LcmEventsServiceSpec extends Specification {
def mockLcmEventsPublisher = Mock(EventsPublisher)
def mockJsonObjectMapper = Mock(JsonObjectMapper)
+ def meterRegistry = new SimpleMeterRegistry()
- def objectUnderTest = new LcmEventsService(mockLcmEventsPublisher, mockJsonObjectMapper)
+ def objectUnderTest = new LcmEventsService(mockLcmEventsPublisher, mockJsonObjectMapper, meterRegistry)
def 'Create and Publish lcm event where events are #scenario'() {
given: 'a cm handle id, Lcm Event, and headers'
def cmHandleId = 'test-cm-handle-id'
def eventId = UUID.randomUUID().toString()
- def lcmEvent = new LcmEvent(eventId: eventId, eventCorrelationId: cmHandleId)
+ def event = getEventWithCmHandleState(ADVISED, READY)
+ def lcmEvent = new LcmEvent(event: event, eventId: eventId, eventCorrelationId: cmHandleId)
and: 'we also have a lcm event header'
def lcmEventHeader = new LcmEventHeader(eventId: eventId, eventCorrelationId: cmHandleId)
and: 'notificationsEnabled is #notificationsEnabled and it will be true as default'
@@ -57,6 +66,16 @@ class LcmEventsServiceSpec extends Specification {
assert eventHeaders.get('eventCorrelationId') == cmHandleId
}
}
+ and: 'metrics are recorded with correct tags'
+ def timer = meterRegistry.find('cps.ncmp.lcm.events.publish').timer()
+ if (notificationsEnabled) {
+ assert timer != null
+ assert timer.count() == expectedTimesMethodCalled
+ def tags = timer.getId().getTags()
+ assert tags.containsAll(Tag.of('oldCmHandleState', ADVISED.value()), Tag.of('newCmHandleState', READY.value()))
+ } else {
+ assert timer == null
+ }
where: 'the following values are used'
scenario | notificationsEnabled || expectedTimesMethodCalled
'enabled' | true || 1
@@ -67,7 +86,8 @@ class LcmEventsServiceSpec extends Specification {
given: 'a cm handle id and Lcm Event and notification enabled'
def cmHandleId = 'test-cm-handle-id'
def eventId = UUID.randomUUID().toString()
- def lcmEvent = new LcmEvent(eventId: eventId, eventCorrelationId: cmHandleId)
+ and: 'event #event'
+ def lcmEvent = new LcmEvent(event: event, eventId: eventId, eventCorrelationId: cmHandleId)
def lcmEventHeader = new LcmEventHeader(eventId: eventId, eventCorrelationId: cmHandleId)
objectUnderTest.notificationsEnabled = true
when: 'publisher set to throw an exception'
@@ -76,6 +96,35 @@ class LcmEventsServiceSpec extends Specification {
objectUnderTest.publishLcmEvent(cmHandleId, lcmEvent, lcmEventHeader)
then: 'the exception is just logged and not bubbled up'
noExceptionThrown()
+ and: 'metrics are recorded with error tags'
+ def timer = meterRegistry.find('cps.ncmp.lcm.events.publish').timer()
+ assert timer != null
+ assert timer.count() == 1
+ def expectedTags = [Tag.of('oldCmHandleState', 'N/A'), Tag.of('newCmHandleState', 'N/A')]
+ def tags = timer.getId().getTags()
+ assert tags.containsAll(expectedTags)
+ where: 'the following values are used'
+ scenario | event
+ 'without values' | new Event()
+ 'without cm handle state' | getEvent()
}
+ def getEvent() {
+ def event = new Event()
+ def values = new Values()
+ event.setOldValues(values)
+ event.setNewValues(values)
+ event
+ }
+
+ def getEventWithCmHandleState(oldCmHandleState, newCmHandleState) {
+ def event = new Event()
+ def advisedCmHandleStateValues = new Values()
+ advisedCmHandleStateValues.setCmHandleState(oldCmHandleState)
+ event.setOldValues(advisedCmHandleStateValues)
+ def readyCmHandleStateValues = new Values()
+ readyCmHandleStateValues.setCmHandleState(newCmHandleState)
+ event.setNewValues(readyCmHandleStateValues)
+ return event
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumerSpec.groovy
index 6db304acd1..c7d0616bb2 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumerSpec.groovy
@@ -49,7 +49,7 @@ class DeviceTrustLevelMessageConsumerSpec extends Specification {
when: 'the event is consumed'
objectUnderTest.deviceTrustLevelListener(consumerRecord)
then: 'cm handles are stored with correct trust level'
- 1 * mockTrustLevelManager.handleUpdateOfDeviceTrustLevel('"ch-1"', TrustLevel.COMPLETE)
+ 1 * mockTrustLevelManager.updateCmHandleTrustLevel('"ch-1"', TrustLevel.COMPLETE)
}
def createTrustLevelEvent(eventPayload) {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDogSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDogSpec.groovy
index 0a34d267c5..32f4503005 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDogSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDogSpec.groovy
@@ -22,8 +22,8 @@ package org.onap.cps.ncmp.impl.inventory.trustlevel
import org.onap.cps.ncmp.api.inventory.models.TrustLevel
import org.onap.cps.ncmp.impl.dmi.DmiRestClient
-import org.onap.cps.ncmp.impl.dmi.UrlTemplateParameters
import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters
import reactor.core.publisher.Mono
import spock.lang.Specification
@@ -46,7 +46,7 @@ class DmiPluginTrustLevelWatchDogSpec extends Specification {
when: 'dmi watch dog method runs'
objectUnderTest.checkDmiAvailability()
then: 'the update delegated to manager'
- numberOfCalls * mockTrustLevelManager.handleUpdateOfDmiTrustLevel('dmi-1', _, newDmiTrustLevel)
+ numberOfCalls * mockTrustLevelManager.updateDmi('dmi-1', _, newDmiTrustLevel)
where: 'the following parameters are used'
dmiHealhStatus | dmiOldTrustLevel | newDmiTrustLevel || numberOfCalls
'UP' | TrustLevel.COMPLETE | TrustLevel.COMPLETE || 0
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfigSpec.groovy
index e79a471015..9391fa0f44 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfigSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfigSpec.groovy
@@ -36,13 +36,17 @@ class TrustLevelCacheConfigSpec extends Specification {
@Autowired
private Map<String, TrustLevel> trustLevelPerCmHandle
+ def cleanupSpec() {
+ Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').shutdown()
+ }
+
def 'Hazelcast cache for trust level per dmi plugin'() {
expect: 'system is able to create an instance of the trust level per dmi plugin cache'
assert null != trustLevelPerDmiPlugin
and: 'there is at least 1 instance'
assert Hazelcast.allHazelcastInstances.size() > 0
and: 'Dmi Plugin Trust Level Cache is present'
- assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceTrustLevelPerDmiPluginMap')
+ assert Hazelcast.allHazelcastInstances.name.contains('cps-and-ncmp-hazelcast-instance-test-config')
}
def 'Hazelcast cache for trust level per cm handle'() {
@@ -51,13 +55,13 @@ class TrustLevelCacheConfigSpec extends Specification {
and: 'there is at least 1 instance'
assert Hazelcast.allHazelcastInstances.size() > 0
and: 'Hazelcast cache instance for trust level is present'
- assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceTrustLevelPerCmHandleMap')
+ assert Hazelcast.allHazelcastInstances.name.contains('cps-and-ncmp-hazelcast-instance-test-config')
}
def 'Trust level cache configurations: #scenario'() {
- when: 'retrieving the cache config for trustLevel'
- def cacheConfig = Hazelcast.getHazelcastInstanceByName(hazelcastInstanceName).config
- then: 'the cache config has the right cluster'
+ given: 'retrieving the common cache config'
+ def cacheConfig = Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').config
+ and: 'the cache config has the right cluster'
assert cacheConfig.clusterName == 'cps-and-ncmp-test-caches'
when: 'retrieving the map config for trustLevel'
def mapConfig = cacheConfig.mapConfigs.get(hazelcastMapConfigName)
@@ -65,14 +69,14 @@ class TrustLevelCacheConfigSpec extends Specification {
assert mapConfig.backupCount == 1
assert mapConfig.asyncBackupCount == 0
where: 'the following caches are used'
- scenario | hazelcastInstanceName | hazelcastMapConfigName
- 'cmhandle map' | 'hazelcastInstanceTrustLevelPerCmHandleMap' | 'trustLevelPerCmHandleCacheConfig'
- 'dmi plugin map' | 'hazelcastInstanceTrustLevelPerDmiPluginMap' | 'trustLevelPerDmiPluginCacheConfig'
+ scenario | hazelcastMapConfigName
+ 'cmhandle map' | 'trustLevelPerCmHandleCacheConfig'
+ 'dmi plugin map' | 'trustLevelPerDmiPluginCacheConfig'
}
def 'Verify deployment network configs for Distributed Caches'() {
given: 'the Trust Level Per Dmi Plugin Cache config'
- def trustLevelDmiPerPluginCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelcastInstanceTrustLevelPerDmiPluginMap').config.networkConfig
+ def trustLevelDmiPerPluginCacheConfig = Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').config.networkConfig
expect: 'system created instance with correct config'
assert trustLevelDmiPerPluginCacheConfig.join.autoDetectionConfig.enabled
assert !trustLevelDmiPerPluginCacheConfig.join.kubernetesConfig.enabled
@@ -80,7 +84,7 @@ class TrustLevelCacheConfigSpec extends Specification {
def 'Verify deployment network configs for Cm Handle Distributed Caches'() {
given: 'the Trust Level Per Cm Handle Cache config'
- def trustLevelPerCmHandlePluginCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelcastInstanceTrustLevelPerCmHandleMap').config.networkConfig
+ def trustLevelPerCmHandlePluginCacheConfig = Hazelcast.getHazelcastInstanceByName('cps-and-ncmp-hazelcast-instance-test-config').config.networkConfig
expect: 'system created instance with correct config'
assert trustLevelPerCmHandlePluginCacheConfig.join.autoDetectionConfig.enabled
assert !trustLevelPerCmHandlePluginCacheConfig.join.kubernetesConfig.enabled
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy
index b5bfbc165c..e0f5f9c4f8 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation
+ * Copyright (C) 2023-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,10 +20,12 @@
package org.onap.cps.ncmp.impl.inventory.trustlevel
+import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
import org.onap.cps.ncmp.api.inventory.models.TrustLevel
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
import org.onap.cps.ncmp.utils.events.CmAvcEventPublisher
+import spock.lang.Ignore
import spock.lang.Specification
class TrustLevelManagerSpec extends Specification {
@@ -35,11 +37,20 @@ class TrustLevelManagerSpec extends Specification {
def mockAttributeValueChangeEventPublisher = Mock(CmAvcEventPublisher)
def objectUnderTest = new TrustLevelManager(trustLevelPerCmHandle, trustLevelPerDmiPlugin, mockInventoryPersistence, mockAttributeValueChangeEventPublisher)
+ def 'Initial dmi registration'() {
+ given: 'a dmi plugin'
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'dmi-1')
+ when: 'method to register to the cache is called'
+ objectUnderTest.registerDmiPlugin(dmiPluginRegistration)
+ then: 'dmi plugin in the cache and trusted'
+ assert trustLevelPerDmiPlugin.get('dmi-1') == TrustLevel.COMPLETE
+ }
+
def 'Initial cm handle registration'() {
given: 'two cm handles: one with no trust level and one trusted'
def cmHandleModelsToBeCreated = ['ch-1': null, 'ch-2': TrustLevel.COMPLETE]
- when: 'the initial registration handled'
- objectUnderTest.handleInitialRegistrationOfTrustLevels(cmHandleModelsToBeCreated)
+ when: 'method to register to the cache is called'
+ objectUnderTest.registerCmHandles(cmHandleModelsToBeCreated)
then: 'no notification sent'
0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_)
and: 'both cm handles are in the cache and are trusted'
@@ -50,8 +61,8 @@ class TrustLevelManagerSpec extends Specification {
def 'Initial cm handle registration with a cm handle that is not trusted'() {
given: 'a not trusted cm handle'
def cmHandleModelsToBeCreated = ['ch-2': TrustLevel.NONE]
- when: 'the initial registration handled'
- objectUnderTest.handleInitialRegistrationOfTrustLevels(cmHandleModelsToBeCreated)
+ when: 'method to register to the cache is called'
+ objectUnderTest.registerCmHandles(cmHandleModelsToBeCreated)
then: 'notification is sent'
1 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_)
}
@@ -62,7 +73,7 @@ class TrustLevelManagerSpec extends Specification {
and: 'a trusted cm handle'
trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE)
when: 'the update is handled'
- objectUnderTest.handleUpdateOfDmiTrustLevel('my-dmi', ['ch-1'], TrustLevel.NONE)
+ objectUnderTest.updateDmi('my-dmi', ['ch-1'], TrustLevel.NONE)
then: 'notification is sent'
1 * mockAttributeValueChangeEventPublisher.publishAvcEvent('ch-1', 'trustLevel', 'COMPLETE', 'NONE')
and: 'the dmi in the cache is not trusted'
@@ -75,54 +86,81 @@ class TrustLevelManagerSpec extends Specification {
and: 'a trusted cm handle'
trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE)
when: 'the update is handled'
- objectUnderTest.handleUpdateOfDmiTrustLevel('my-dmi', ['ch-1'], TrustLevel.COMPLETE)
+ objectUnderTest.updateDmi('my-dmi', ['ch-1'], TrustLevel.COMPLETE)
then: 'no notification is sent'
0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_)
and: 'the dmi in the cache is trusted'
assert trustLevelPerDmiPlugin.get('my-dmi') == TrustLevel.COMPLETE
}
- def 'Device trust level updated'() {
+ def 'CmHandle trust level updated'() {
given: 'a non trusted cm handle'
trustLevelPerCmHandle.put('ch-1', TrustLevel.NONE)
and: 'a trusted dmi plugin'
trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.COMPLETE)
and: 'inventory persistence service returns yang model cm handle'
mockInventoryPersistence.getYangModelCmHandle('ch-1') >> new YangModelCmHandle(id: 'ch-1', dmiDataServiceName: 'my-dmi')
- when: 'update of device to COMPLETE trust level handled'
- objectUnderTest.handleUpdateOfDeviceTrustLevel('ch-1', TrustLevel.COMPLETE)
+ when: 'update of CmHandle to COMPLETE trust level handled'
+ objectUnderTest.updateCmHandleTrustLevel('ch-1', TrustLevel.COMPLETE)
then: 'the cm handle in the cache is trusted'
assert trustLevelPerCmHandle.get('ch-1', TrustLevel.COMPLETE)
and: 'notification is sent'
1 * mockAttributeValueChangeEventPublisher.publishAvcEvent('ch-1', 'trustLevel', 'NONE', 'COMPLETE')
}
- def 'Device trust level updated with same value'() {
+ def 'CmHandle trust level updated with same value'() {
given: 'a non trusted cm handle'
trustLevelPerCmHandle.put('ch-1', TrustLevel.NONE)
and: 'a trusted dmi plugin'
trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.COMPLETE)
and: 'inventory persistence service returns yang model cm handle'
mockInventoryPersistence.getYangModelCmHandle('ch-1') >> new YangModelCmHandle(id: 'ch-1', dmiDataServiceName: 'my-dmi')
- when: 'update of device trust to the same level (NONE)'
- objectUnderTest.handleUpdateOfDeviceTrustLevel('ch-1', TrustLevel.NONE)
+ when: 'update of CmHandle trust to the same level (NONE)'
+ objectUnderTest.updateCmHandleTrustLevel('ch-1', TrustLevel.NONE)
then: 'the cm handle in the cache is not trusted'
assert trustLevelPerCmHandle.get('ch-1', TrustLevel.NONE)
and: 'no notification is sent'
0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_)
}
- def 'Dmi trust level restored to complete with non trusted device'() {
+ def 'Dmi trust level restored to complete with non trusted CmHandle'() {
given: 'a non trusted dmi'
trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.NONE)
- and: 'a non trusted device'
+ and: 'a non trusted CmHandle'
trustLevelPerCmHandle.put('ch-1', TrustLevel.NONE)
when: 'restore the dmi trust level to COMPLETE'
- objectUnderTest.handleUpdateOfDmiTrustLevel('my-dmi', ['ch-1'], TrustLevel.COMPLETE)
+ objectUnderTest.updateDmi('my-dmi', ['ch-1'], TrustLevel.COMPLETE)
then: 'the cm handle in the cache is still NONE'
assert trustLevelPerCmHandle.get('ch-1') == TrustLevel.NONE
and: 'no notification is sent'
0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_)
}
+ @Ignore
+ // TODO: CPS-2375
+ def 'Select effective trust level among CmHandle and dmi plugin'() {
+ given: 'a non trusted dmi'
+ trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.NONE)
+ and: 'a trusted CmHandle'
+ trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE)
+ when: 'effective trust level selected'
+ def effectiveTrustLevel = objectUnderTest.getEffectiveTrustLevel('ch-1')
+ then: 'effective trust level is trusted'
+ assert effectiveTrustLevel == TrustLevel.NONE
+ }
+
+ def 'Select effective trust level when the trust level caches are empty (restart case)'() {
+ expect: 'effective trust level is NONE when cm-1 does not exist in the cache'
+ assert objectUnderTest.getEffectiveTrustLevel('ch-1') == TrustLevel.NONE
+ }
+
+ def 'CmHandle trust level removed'() {
+ given: 'a cm handle'
+ trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE)
+ when: 'the remove is handled'
+ objectUnderTest.removeCmHandles(['ch-1'])
+ then: 'cm handle removed from the cache'
+ assert trustLevelPerCmHandle.get('ch-1') == null
+ }
+
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfigurationSpec.groovy
new file mode 100644
index 0000000000..cf5e1a383c
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfigurationSpec.groovy
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.policyexecutor
+
+import org.onap.cps.ncmp.config.PolicyExecutorHttpClientConfig
+import org.springframework.boot.context.properties.EnableConfigurationProperties
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.web.reactive.function.client.WebClient
+import spock.lang.Specification
+
+@SpringBootTest
+@ContextConfiguration(classes = [PolicyExecutorHttpClientConfig])
+@EnableConfigurationProperties
+class PolicyExecutorWebClientConfigurationSpec extends Specification {
+
+ def webClientBuilder = Mock(WebClient.Builder) {
+ defaultHeaders(_) >> it
+ clientConnector(_) >> it
+ codecs(_) >> it
+ build() >> Mock(WebClient)
+ }
+
+ def httpClientConfiguration = Spy(PolicyExecutorHttpClientConfig.class)
+
+ def objectUnderTest = new PolicyExecutorWebClientConfiguration(httpClientConfiguration)
+
+ def 'Web client policy executor.'() {
+ when: 'create a web client for policy executor'
+ def result = objectUnderTest.policyExecutorWebClient(webClientBuilder)
+ then: 'a web client is created successfully'
+ assert result != null
+ assert result instanceof WebClient
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy
index ad84495825..a497b4554a 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy
@@ -63,4 +63,18 @@ class AlternateIdMatcherSpec extends Specification {
'no match for other child' | '/a/c'
'no match at all' | '/x/y'
}
+
+ def 'Get cmHandle id from passed cmHandleReference (cmHandleId scenario)' () {
+ when: 'a cmHandleCmReference is passed in'
+ def result = objectUnderTest.getCmHandleId(cmHandleReference)
+ then: 'the inventory persistence service returns a cm handle (or not)'
+ mockInventoryPersistence.isExistingCmHandleId(cmHandleReference) >> existingCmHandleIdResponse
+ mockInventoryPersistence.getCmHandleDataNodeByAlternateId(cmHandleReference) >> alternateIdGetResponse
+ and: 'correct result is returned'
+ assert result == cmHandleReference
+ where:
+ cmHandleReference | existingCmHandleIdResponse | alternateIdGetResponse
+ 'ch-1' | true | ''
+ 'alt-1' | false | new DataNode(leaves: [id:'alt-1'])
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiServiceUrlTemplateBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilderSpec.groovy
index 9e1b37023b..9e051156f7 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiServiceUrlTemplateBuilderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilderSpec.groovy
@@ -18,14 +18,13 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.impl.dmi
-
+package org.onap.cps.ncmp.impl.utils.http
import spock.lang.Specification
-class DmiServiceUrlTemplateBuilderSpec extends Specification {
+class RestServiceUrlTemplateBuilderSpec extends Specification {
- def objectUnderTest = new DmiServiceUrlTemplateBuilder()
+ def objectUnderTest = new RestServiceUrlTemplateBuilder()
def 'Build URL template parameters with (variable) path segments and query parameters.'() {
given: 'the URL details are given to the builder'
@@ -35,9 +34,9 @@ class DmiServiceUrlTemplateBuilderSpec extends Specification {
objectUnderTest.queryParameter('param1', 'abc')
objectUnderTest.queryParameter('param2', 'value?with#special:characters')
when: 'the URL template parameters are created'
- def result = objectUnderTest.createUrlTemplateParameters('myDmiServer', 'myBasePath')
+ def result = objectUnderTest.createUrlTemplateParameters('myServer', 'myBasePath')
then: 'the URL template contains variable names instead of value and un-encoded fixed segment'
- assert result.urlTemplate == 'myDmiServer/myBasePath/v1/segment/{myVariableSegment}/segment?with:special&characters?param1={param1}&param2={param2}'
+ assert result.urlTemplate == 'myServer/myBasePath/v1/segment/{myVariableSegment}/segment?with:special&characters?param1={param1}&param2={param2}'
and: 'URL variables contains name and un-encoded value pairs'
assert result.urlVariables == ['myVariableSegment': 'someValue', 'param1': 'abc', 'param2': 'value?with#special:characters']
}
@@ -46,7 +45,7 @@ class DmiServiceUrlTemplateBuilderSpec extends Specification {
given: 'the query parameter is given to the builder'
objectUnderTest.queryParameter('my&param', 'special&characters=are?not\\encoded')
when: 'the URL template parameters are created'
- def result = objectUnderTest.createUrlTemplateParameters('myDmiServer', 'myBasePath')
+ def result = objectUnderTest.createUrlTemplateParameters('myServer', 'myBasePath')
then: 'Special characters are not encoded'
assert result.urlVariables == ['my&param': 'special&characters=are?not\\encoded']
}
@@ -55,7 +54,7 @@ class DmiServiceUrlTemplateBuilderSpec extends Specification {
when: 'the query parameter is given to the builder'
objectUnderTest.queryParameter('param', value)
and: 'the URL template parameters are create'
- def result = objectUnderTest.createUrlTemplateParameters('myDmiServer', 'myBasePath')
+ def result = objectUnderTest.createUrlTemplateParameters('myServer', 'myBasePath')
then: 'no parameter gets added'
assert result.urlVariables.isEmpty()
where: 'the following parameter values are used'
diff --git a/cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/WebClientBuilderTestConfig.java b/cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/WebClientBuilderTestConfig.java
new file mode 100644
index 0000000000..2f6b270076
--- /dev/null
+++ b/cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/WebClientBuilderTestConfig.java
@@ -0,0 +1,40 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.utils;
+
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@TestConfiguration
+public class WebClientBuilderTestConfig {
+
+ /**
+ * Configures and creates a web client builder bean to make it accessible for the Spring Boot Test Context.
+ *
+ * @return a WebClient Builder instance.
+ */
+ @Bean
+ public WebClient.Builder webClientBuilder() {
+ return WebClient.builder();
+ }
+
+}
diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml
index f0790dda4b..12db639633 100644
--- a/cps-ncmp-service/src/test/resources/application.yml
+++ b/cps-ncmp-service/src/test/resources/application.yml
@@ -16,6 +16,15 @@
# SPDX-License-Identifier: Apache-2.0
# ============LICENSE_END=========================================================
+cps:
+ tracing:
+ sampler:
+ jaeger_remote:
+ endpoint: http://jaeger-Remote-test-url
+ exporter:
+ endpoint: http://exporter-test-url
+ enabled: true
+
spring:
kafka:
producer:
@@ -38,15 +47,26 @@ ncmp:
dmi:
httpclient:
data-services:
- pendingAcquireMaxCount: 22
- connectionTimeoutInSeconds: 123
- maximumInMemorySizeInMegabytes: 7
- readTimeoutInSeconds: 33
+ maximumInMemorySizeInMegabytes: 1
+ maximumConnectionsTotal: 2
+ pendingAcquireMaxCount: 3
+ connectionTimeoutInSeconds: 4
+ readTimeoutInSeconds: 5
+ writeTimeoutInSeconds: 6
model-services:
- pendingAcquireMaxCount: 44
- connectionTimeoutInSeconds: 456
- maximumInMemorySizeInMegabytes: 8
- maximumConnectionsTotal: 111
+ maximumInMemorySizeInMegabytes: 11
+ maximumConnectionsTotal: 12
+ pendingAcquireMaxCount: 13
+ connectionTimeoutInSeconds: 14
+ readTimeoutInSeconds: 15
+ writeTimeoutInSeconds: 16
+ healthCheckServices:
+ maximumInMemorySizeInMegabytes: 21
+ maximumConnectionsTotal: 22
+ pendingAcquireMaxCount: 23
+ connectionTimeoutInSeconds: 24
+ readTimeoutInSeconds: 25
+ writeTimeoutInSeconds: 26
auth:
username: some-user
password: some-password
@@ -63,13 +83,23 @@ ncmp:
policy-executor:
enabled: true
+ defaultDecision: "some default decision"
server:
- address: "http://localhost"
- port: "8785"
+ address: http://localhost
+ port: 8785
+ httpclient:
+ all-services:
+ maximumInMemorySizeInMegabytes: 31
+ maximumConnectionsTotal: 32
+ pendingAcquireMaxCount: 33
+ connectionTimeoutInSeconds: 34
+ readTimeoutInSeconds: 35
+ writeTimeoutInSeconds: 36
# Custom Hazelcast Config.
hazelcast:
cluster-name: "cps-and-ncmp-test-caches"
+ instance-config-name: "cps-and-ncmp-hazelcast-instance-test-config"
mode:
kubernetes:
enabled: false
diff --git a/cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json b/cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json
index 5b297c86c2..1dc14bd65e 100644
--- a/cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json
+++ b/cps-ncmp-service/src/test/resources/sampleAvcInputEvent.json
@@ -10,11 +10,8 @@
"operation":"replace",
"target":"ran-network:ran-network/NearRTRIC[@id='22']/GNBCUCPFunction[@id='cucpserver2']/NRCellCU[@id='15549']/NRCellRelation[@id='14427']",
"value":{
- "attributes":[
- {
- "isHoAllowed":true
- }
- ]
+ "color": "yellow",
+ "name": "Apple"
}
},
{
diff --git a/cps-parent/pom.xml b/cps-parent/pom.xml
index ba639441e7..f97a40b223 100644
--- a/cps-parent/pom.xml
+++ b/cps-parent/pom.xml
@@ -32,7 +32,7 @@
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
@@ -154,12 +154,12 @@
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
- <version>4.4.2</version>
+ <version>4.8.6.4</version>
<dependencies>
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs</artifactId>
- <version>4.2.3</version>
+ <version>4.8.6</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
@@ -410,30 +410,6 @@
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</plugin>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>exec-maven-plugin</artifactId>
- <version>1.6.0</version>
- <executions>
- <execution>
- <id>generate-csv</id>
- <phase>prepare-package</phase>
- <goals>
- <goal>exec</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <executable>${script.executor}</executable>
- <workingDirectory>${parent.directory}/cps-ri/src/main/resources/</workingDirectory>
- <arguments>
- <argument>yangResourceCsvGenerator.py</argument>
- <argument>dmi-registry@2021-12-13</argument>
- <argument>dmi-registry@2022-02-10</argument>
- <argument>dmi-registry@2022-05-10</argument>
- </arguments>
- </configuration>
- </plugin>
</plugins>
</build>
</project>
diff --git a/cps-path-parser/pom.xml b/cps-path-parser/pom.xml
index 633ec6bb80..57b1e6b247 100644
--- a/cps-path-parser/pom.xml
+++ b/cps-path-parser/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
diff --git a/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4
index 3aef120fed..74b99feb33 100644
--- a/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4
+++ b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2023 TechMahindra Ltd
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,23 +27,23 @@
grammar CpsPath ;
-cpsPath : ( prefix | descendant | incorrectPrefix ) multipleLeafConditions? textFunctionCondition? containsFunctionCondition? ancestorAxis? invalidPostFix?;
+cpsPath : ( prefix | descendant ) multipleLeafConditions? textFunctionCondition? containsFunctionCondition? ancestorAxis? EOF ;
-ancestorAxis : SLASH KW_ANCESTOR COLONCOLON ancestorPath ;
+slash : SLASH ;
-ancestorPath : yangElement ( SLASH yangElement)* ;
+ancestorAxis : KW_ANCESTOR_AXIS_PREFIX ancestorPath ;
-textFunctionCondition : SLASH leafName OB KW_TEXT_FUNCTION EQ StringLiteral CB ;
+ancestorPath : yangElement ( slash yangElement)* ;
-containsFunctionCondition : OB KW_CONTAINS_FUNCTION OP AT leafName COMMA StringLiteral CP CB ;
+textFunctionCondition : slash leafName OB KW_TEXT_FUNCTION EQ StringLiteral CB ;
-parent : ( SLASH yangElement)* ;
+containsFunctionCondition : OB KW_CONTAINS_FUNCTION OP AT leafName COMMA StringLiteral CP CB ;
-prefix : parent SLASH containerName ;
+parent : ( slash yangElement)* ;
-descendant : SLASH prefix ;
+prefix : parent slash containerName ;
-incorrectPrefix : SLASH SLASH SLASH+ ;
+descendant : slash prefix ;
yangElement : containerName listElementRef? ;
@@ -61,8 +61,6 @@ booleanOperators : ( KW_AND | KW_OR ) ;
comparativeOperators : ( EQ | GT | LT | GE | LE ) ;
-invalidPostFix : (AT | CB | COLONCOLON | comparativeOperators ).+ ;
-
/*
* Lexer Rules
* Most of the lexer rules below are inspired by
@@ -89,10 +87,11 @@ KW_ANCESTOR : 'ancestor' ;
KW_AND : 'and' ;
KW_TEXT_FUNCTION: 'text()' ;
KW_OR : 'or' ;
-KW_CONTAINS_FUNCTION: 'contains' ;
+KW_CONTAINS_FUNCTION : 'contains' ;
+KW_ANCESTOR_AXIS_PREFIX : SLASH KW_ANCESTOR COLONCOLON ;
IntegerLiteral : FragDigits ;
-// Add below type definitions for leafvalue comparision in https://jira.onap.org/browse/CPS-440
+// Add below type definitions for leafvalue comparision in https://lf-onap.atlassian.net/CPS-440
DecimalLiteral : ('.' FragDigits) | (FragDigits '.' [0-9]*) ;
DoubleLiteral : (('.' FragDigits) | (FragDigits ('.' [0-9]*)?)) [eE] [+-]? FragDigits ;
StringLiteral : '"' (~["] | FragEscapeQuot)* '"' | '\'' (~['] | FragEscapeApos)* '\'' ;
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 0bb09235ff..ed7dbecc18 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
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2023 TechMahindra Ltd
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,7 +29,6 @@ import org.onap.cps.cpspath.parser.antlr4.CpsPathBaseListener;
import org.onap.cps.cpspath.parser.antlr4.CpsPathParser;
import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.AncestorAxisContext;
import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.DescendantContext;
-import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.IncorrectPrefixContext;
import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.LeafConditionContext;
import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.MultipleLeafConditionsContext;
import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.PrefixContext;
@@ -37,17 +36,19 @@ import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.TextFunctionConditionCon
public class CpsPathBuilder extends CpsPathBaseListener {
+ private static final String NO_PARENT_PATH = "";
+
private static final String OPEN_BRACKET = "[";
private static final String CLOSE_BRACKET = "]";
private final CpsPathQuery cpsPathQuery = new CpsPathQuery();
- private final List<CpsPathQuery.DataLeaf> leavesData = new ArrayList<>();
+ private final List<CpsPathQuery.LeafCondition> leafConditions = new ArrayList<>();
private final StringBuilder normalizedXpathBuilder = new StringBuilder();
- private final StringBuilder normalizedAncestorPathBuilder = new StringBuilder();
+ private int startIndexOfAncestorSchemaNodeIdentifier = 0;
private boolean processingAncestorAxis = false;
@@ -55,11 +56,9 @@ public class CpsPathBuilder extends CpsPathBaseListener {
private final List<String> booleanOperators = new ArrayList<>();
- private final List<String> comparativeOperators = new ArrayList<>();
-
@Override
- public void exitInvalidPostFix(final CpsPathParser.InvalidPostFixContext ctx) {
- throw new PathParsingException(ctx.getText());
+ public void exitSlash(final CpsPathParser.SlashContext ctx) {
+ normalizedXpathBuilder.append("/");
}
@Override
@@ -69,16 +68,19 @@ public class CpsPathBuilder extends CpsPathBaseListener {
@Override
public void exitParent(final CpsPathParser.ParentContext ctx) {
- cpsPathQuery.setNormalizedParentPath(normalizedXpathBuilder.toString());
- }
-
- @Override
- public void exitIncorrectPrefix(final IncorrectPrefixContext ctx) {
- throw new PathParsingException("CPS path can only start with one or two slashes (/)");
+ final String normalizedParentPath;
+ if (normalizedXpathBuilder.toString().equals("/")) {
+ normalizedParentPath = NO_PARENT_PATH;
+ } else {
+ normalizedParentPath = normalizedXpathBuilder.toString();
+ }
+ cpsPathQuery.setNormalizedParentPath(normalizedParentPath);
}
@Override
public void exitLeafCondition(final LeafConditionContext ctx) {
+ final String leafName = ctx.leafName().getText();
+ final String operator = ctx.comparativeOperators().getText();
final Object comparisonValue;
if (ctx.IntegerLiteral() != null) {
comparisonValue = Integer.valueOf(ctx.IntegerLiteral().getText());
@@ -87,7 +89,7 @@ public class CpsPathBuilder extends CpsPathBaseListener {
} else {
throw new PathParsingException("Unsupported comparison value encountered in expression" + ctx.getText());
}
- leafContext(ctx.leafName(), comparisonValue);
+ leafContext(leafName, operator, comparisonValue);
}
@Override
@@ -96,40 +98,36 @@ public class CpsPathBuilder extends CpsPathBaseListener {
}
@Override
- public void exitComparativeOperators(final CpsPathParser.ComparativeOperatorsContext ctx) {
- comparativeOperators.add(ctx.getText());
- }
-
- @Override
public void exitDescendant(final DescendantContext ctx) {
cpsPathQuery.setCpsPathPrefixType(DESCENDANT);
- cpsPathQuery.setDescendantName(normalizedXpathBuilder.substring(1));
- normalizedXpathBuilder.insert(0, "/");
+ cpsPathQuery.setDescendantName(normalizedXpathBuilder.substring(2));
}
@Override
public void enterMultipleLeafConditions(final MultipleLeafConditionsContext ctx) {
normalizedXpathBuilder.append(OPEN_BRACKET);
- leavesData.clear();
+ leafConditions.clear();
booleanOperators.clear();
- comparativeOperators.clear();
}
@Override
public void exitMultipleLeafConditions(final MultipleLeafConditionsContext ctx) {
normalizedXpathBuilder.append(CLOSE_BRACKET);
- cpsPathQuery.setLeavesData(leavesData);
+ cpsPathQuery.setLeafConditions(leafConditions);
}
@Override
public void enterAncestorAxis(final AncestorAxisContext ctx) {
processingAncestorAxis = true;
+ normalizedXpathBuilder.append("/ancestor::");
+ startIndexOfAncestorSchemaNodeIdentifier = normalizedXpathBuilder.length();
}
@Override
public void exitAncestorAxis(final AncestorAxisContext ctx) {
- cpsPathQuery.setAncestorSchemaNodeIdentifier(normalizedAncestorPathBuilder.substring(1));
processingAncestorAxis = false;
+ cpsPathQuery.setAncestorSchemaNodeIdentifier(
+ normalizedXpathBuilder.substring(startIndexOfAncestorSchemaNodeIdentifier));
}
@Override
@@ -147,52 +145,36 @@ public class CpsPathBuilder extends CpsPathBaseListener {
@Override
public void enterListElementRef(final CpsPathParser.ListElementRefContext ctx) {
normalizedXpathBuilder.append(OPEN_BRACKET);
- if (processingAncestorAxis) {
- normalizedAncestorPathBuilder.append(OPEN_BRACKET);
- }
}
@Override
public void exitListElementRef(final CpsPathParser.ListElementRefContext ctx) {
normalizedXpathBuilder.append(CLOSE_BRACKET);
- if (processingAncestorAxis) {
- normalizedAncestorPathBuilder.append(CLOSE_BRACKET);
- }
}
CpsPathQuery build() {
cpsPathQuery.setNormalizedXpath(normalizedXpathBuilder.toString());
cpsPathQuery.setContainerNames(containerNames);
cpsPathQuery.setBooleanOperators(booleanOperators);
- cpsPathQuery.setComparativeOperators(comparativeOperators);
- if (cpsPathQuery.hasAncestorAxis() && cpsPathQuery.getXpathPrefix()
- .endsWith("/" + cpsPathQuery.getAncestorSchemaNodeIdentifier())) {
- cpsPathQuery.setAncestorSchemaNodeIdentifier("");
- }
return cpsPathQuery;
}
@Override
public void exitContainerName(final CpsPathParser.ContainerNameContext ctx) {
final String containerName = ctx.getText();
- normalizedXpathBuilder.append("/")
- .append(containerName);
- containerNames.add(containerName);
- if (processingAncestorAxis) {
- normalizedAncestorPathBuilder.append("/").append(containerName);
+ normalizedXpathBuilder.append(containerName);
+ if (!processingAncestorAxis) {
+ containerNames.add(containerName);
}
}
- private void leafContext(final CpsPathParser.LeafNameContext ctx, final Object comparisonValue) {
- leavesData.add(new CpsPathQuery.DataLeaf(ctx.getText(), comparisonValue));
- appendCondition(normalizedXpathBuilder, ctx.getText(), comparisonValue);
- if (processingAncestorAxis) {
- appendCondition(normalizedAncestorPathBuilder, ctx.getText(), comparisonValue);
- }
+ private void leafContext(final String leafName, final String operator, final Object comparisonValue) {
+ leafConditions.add(new CpsPathQuery.LeafCondition(leafName, operator, comparisonValue));
+ appendCondition(normalizedXpathBuilder, leafName, operator, comparisonValue);
}
private void appendCondition(final StringBuilder currentNormalizedPathBuilder, final String name,
- final Object value) {
+ final String operator, final Object value) {
final char lastCharacter = currentNormalizedPathBuilder.charAt(currentNormalizedPathBuilder.length() - 1);
final boolean isStartOfExpression = lastCharacter == '[';
if (!isStartOfExpression) {
@@ -200,7 +182,7 @@ public class CpsPathBuilder extends CpsPathBaseListener {
}
currentNormalizedPathBuilder.append("@")
.append(name)
- .append(getLastElement(comparativeOperators))
+ .append(operator)
.append("'")
.append(value.toString().replace("'", "''"))
.append("'");
diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java
index f98df05a28..03602b64f6 100644
--- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java
+++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2023 TechMahindra Ltd
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,8 +25,6 @@ import static org.onap.cps.cpspath.parser.CpsPathPrefixType.ABSOLUTE;
import java.util.List;
import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@@ -40,12 +38,11 @@ public class CpsPathQuery {
private List<String> containerNames;
private CpsPathPrefixType cpsPathPrefixType = ABSOLUTE;
private String descendantName;
- private List<DataLeaf> leavesData;
+ private List<LeafCondition> leafConditions;
private String ancestorSchemaNodeIdentifier = "";
private String textFunctionConditionLeafName;
private String textFunctionConditionValue;
private List<String> booleanOperators;
- private List<String> comparativeOperators;
private String containsFunctionConditionLeafName;
private String containsFunctionConditionValue;
@@ -74,7 +71,7 @@ public class CpsPathQuery {
* @return boolean value.
*/
public boolean hasLeafConditions() {
- return leavesData != null;
+ return leafConditions != null;
}
/**
@@ -104,11 +101,6 @@ public class CpsPathQuery {
return cpsPathPrefixType == ABSOLUTE && hasLeafConditions();
}
- @Getter
- @EqualsAndHashCode
- @AllArgsConstructor
- public static class DataLeaf {
- private final String name;
- private final Object value;
- }
+ public record LeafCondition(String name, String operator, Object value) { }
+
}
diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java
index bde9b0638f..4ede0d9c90 100644
--- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java
+++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,12 +59,10 @@ public class CpsPathUtil {
return getCpsPathBuilder(xpathSource).build().getNormalizedParentPath();
}
- public static String[] getXpathNodeIdSequence(final String xpathSource) {
- final List<String> containerNames = getCpsPathBuilder(xpathSource).build().getContainerNames();
- return containerNames.toArray(new String[containerNames.size()]);
+ public static List<String> getXpathNodeIdSequence(final String xpathSource) {
+ return getCpsPathBuilder(xpathSource).build().getContainerNames();
}
-
/**
* Returns boolean indicating xpath is an absolute path to a list element.
*
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 15f6b11823..16430d2fa5 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
@@ -48,8 +48,8 @@ class CpsPathQuerySpec extends Specification {
and: 'the right query parameters are set'
assert result.xpathPrefix == expectedXpathPrefix
assert result.hasLeafConditions()
- assert result.leavesData[0].name == expectedLeafName
- assert result.leavesData[0].value == expectedLeafValue
+ assert result.leafConditions[0].name() == expectedLeafName
+ assert result.leafConditions[0].value() == expectedLeafValue
where: 'the following data is used'
scenario | cpsPath || expectedXpathPrefix | expectedLeafName | expectedLeafValue
'leaf of type String' | '/parent/child[@common-leaf-name="common-leaf-value"]' || '/parent/child' | 'common-leaf-name' | 'common-leaf-value'
@@ -91,13 +91,14 @@ class CpsPathQuerySpec extends Specification {
'descendant with leaf condition has "<" operator' | '//cps-path[@key<10]' || "//cps-path[@key<'10']"
'descendant with leaf condition has ">=" operator' | '//cps-path[@key>=8]' || "//cps-path[@key>='8']"
'descendant with leaf condition has "<=" operator' | '//cps-path[@key<=12]' || "//cps-path[@key<='12']"
- 'descendant with leaf value and ancestor' | '//cps-path[@key=1]/ancestor:parent[@key=1]' || "//cps-path[@key='1']/ancestor:parent[@key='1']"
+ 'descendant with leaf value and ancestor' | '//cps-path[@key=1]/ancestor::parent[@key=1]' || "//cps-path[@key='1']/ancestor::parent[@key='1']"
'parent & child' | '/parent/child' || '/parent/child'
'parent leaf of type Integer & child' | '/parent/child[@code=1]/child2' || "/parent/child[@code='1']/child2"
'parent leaf with double quotes' | '/parent/child[@code="1"]/child2' || "/parent/child[@code='1']/child2"
'parent leaf with double quotes inside single quotes' | '/parent/child[@code=\'"1"\']/child2' || "/parent/child[@code='\"1\"']/child2"
'parent leaf with single quotes inside double quotes' | '/parent/child[@code="\'1\'"]/child2' || "/parent/child[@code='''1''']/child2"
'leaf with single quotes inside double quotes' | '/parent/child[@code="\'1\'"]' || "/parent/child[@code='''1''']"
+ 'leaf with single quotes inside single quotes' | "/parent/child[@code='I''m quoted']" || "/parent/child[@code='I''m quoted']"
'leaf with more than one attribute' | '/parent/child[@key1=1 and @key2="abc"]' || "/parent/child[@key1='1' and @key2='abc']"
'parent & child with more than one attribute' | '/parent/child[@key1=1 and @key2="abc"]/child2' || "/parent/child[@key1='1' and @key2='abc']/child2"
'leaf with more than one attribute has OR operator' | '/parent/child[@key1=1 or @key2="abc"]' || "/parent/child[@key1='1' or @key2='abc']"
@@ -123,11 +124,11 @@ class CpsPathQuerySpec extends Specification {
when: 'the given cps path is parsed'
def result = CpsPathQuery.createFrom(cpsPath)
then: 'the expected number of leaves are returned'
- result.leavesData.size() == expectedNumberOfLeaves
+ result.leafConditions.size() == expectedNumberOfLeaves
and: 'the given operator(s) returns in the correct order'
result.booleanOperators == expectedOperators
and: 'the given comparativeOperator(s) returns in the correct order'
- result.comparativeOperators == expectedComparativeOperator
+ result.leafConditions.operator == expectedComparativeOperator
where: 'the following data is used'
cpsPath || expectedNumberOfLeaves || expectedOperators || expectedComparativeOperator
'/parent[@code=1]/child[@common-leaf-name-int=5]' || 1 || [] || ['=']
@@ -234,40 +235,23 @@ class CpsPathQuerySpec extends Specification {
when: 'the given cps path is parsed using multiple conditions on same leaf'
def result = CpsPathQuery.createFrom('/test[@same-name="value1" or @same-name="value2"]')
then: 'two leaves are present with correct values'
- assert result.leavesData.size() == 2
- assert result.leavesData[0].name == "same-name"
- assert result.leavesData[0].value == "value1"
- assert result.leavesData[1].name == "same-name"
- assert result.leavesData[1].value == "value2"
+ assert result.leafConditions.size() == 2
+ assert result.leafConditions[0].name == "same-name"
+ assert result.leafConditions[0].value == "value1"
+ assert result.leafConditions[1].name == "same-name"
+ assert result.leafConditions[1].value == "value2"
}
def 'Ordering of data leaves is preserved.'() {
when: 'the given cps path is parsed'
def result = CpsPathQuery.createFrom(cpsPath)
then: 'the order of the data leaves is preserved'
- assert result.leavesData[0].name == expectedFirstLeafName
- assert result.leavesData[1].name == expectedSecondLeafName
+ assert result.leafConditions[0].name == expectedFirstLeafName
+ assert result.leafConditions[1].name == expectedSecondLeafName
where: 'the following data is used'
cpsPath || expectedFirstLeafName | expectedSecondLeafName
'/test[@name1="value1" and @name2="value2"]' || 'name1' | 'name2'
'/test[@name2="value2" and @name1="value1"]' || 'name2' | 'name1'
}
- def 'Ancestor axis matching prefix'() {
- when: 'building a cps path query'
- def result = parseXPathAndBuild(xpath)
- then: 'ancestor axis is removed when same as prefix'
- assert result.hasAncestorAxis() == expectAncestorAxis
- where: 'the following xpaths are used'
- xpath || expectAncestorAxis
- '//abc/def/ancestor::abc' || true
- '//abc/def/ancestor::def' || false
- '//abc/def/ancestor::ef' || true
- }
-
- def parseXPathAndBuild(xpath) {
- def cpsPathBuilder = CpsPathUtil.getCpsPathBuilder(xpath)
- cpsPathBuilder.build()
- }
-
}
diff --git a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy
index d62f337b75..29bb3c7b58 100644
--- a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy
+++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,21 +36,40 @@ class CpsPathUtilSpec extends Specification {
'single quotes' | "/parent/child[@common-leaf-name='123']"
}
- def 'Normalized parent xpaths'() {
- when: 'a given xpath with #scenario is parsed'
- def result = CpsPathUtil.getNormalizedParentXpath(xpath)
+ def 'Normalized parent paths of absolute paths'() {
+ when: 'a given cps path is parsed'
+ def result = CpsPathUtil.getNormalizedParentXpath(cpsPath)
then: 'the result is the expected parent path'
assert result == expectedParentPath
- where: 'the following xpaths are used'
- scenario | xpath || expectedParentPath
- 'no child' | '/parent' || ''
- 'child and parent' | '/parent/child' || '/parent'
- 'grand child' | '/parent/child/grandChild' || '/parent/child'
- 'parent & top is list element' | '/parent[@id=1]/child' || "/parent[@id='1']"
- 'parent is list element' | '/parent/child[@id=1]/grandChild' || "/parent/child[@id='1']"
- 'parent is list element with /' | "/parent/child[@id='a/b']/grandChild" || "/parent/child[@id='a/b']"
- 'parent is list element with [' | "/parent/child[@id='a[b']/grandChild" || "/parent/child[@id='a[b']"
- 'parent is list element using "' | '/parent/child[@id="x"]/grandChild' || "/parent/child[@id='x']"
+ where: 'the following absolute cps paths are used'
+ cpsPath || expectedParentPath
+ '/parent' || ''
+ '/parent/child' || '/parent'
+ '/parent/child/grandChild' || '/parent/child'
+ '/parent[@id=1]/child' || "/parent[@id='1']"
+ '/parent/child[@id=1]/grandChild' || "/parent/child[@id='1']"
+ '/parent/child/grandChild[@id="x"]' || "/parent/child"
+ '/parent/ancestor::grandparent' || ''
+ '/parent/child/ancestor::grandparent' || '/parent'
+ '/parent/child/name[text()="value"]' || '/parent'
+ }
+
+ def 'Normalized parent paths of descendant paths'() {
+ when: 'a given cps path is parsed'
+ def result = CpsPathUtil.getNormalizedParentXpath(cpsPath)
+ then: 'the result is the expected parent path'
+ assert result == expectedParentPath
+ where: 'the following descendant cps paths are used'
+ cpsPath || expectedParentPath
+ '//parent' || ''
+ '//parent/child' || '//parent'
+ '//parent/child/grandChild' || '//parent/child'
+ '//parent[@id=1]/child' || "//parent[@id='1']"
+ '//parent/child[@id=1]/grandChild' || "//parent/child[@id='1']"
+ '//parent/child/grandChild[@id="x"]' || "//parent/child"
+ '//parent/ancestor::grandparent' || ''
+ '//parent/child/ancestor::grandparent' || '//parent'
+ '//parent/child/name[text()="value"]' || '//parent'
}
def 'Get node ID sequence for given xpath'() {
@@ -67,17 +86,19 @@ class CpsPathUtilSpec extends Specification {
'parent is list element' | '/parent/child[@id=1]/grandChild' || ["parent","child","grandChild"]
'parent is list element with /' | "/parent/child[@id='a/b']/grandChild" || ["parent","child","grandChild"]
'parent is list element with [' | "/parent/child[@id='a[b']/grandChild" || ["parent","child","grandChild"]
+ 'does not include ancestor node' | '/parent/child/ancestor::grandparent' || ["parent","child"]
}
def 'Recognizing (absolute) xpaths to List elements'() {
expect: 'check for list returns the correct values'
assert CpsPathUtil.isPathToListElement(xpath) == expectList
where: 'the following xpaths are used'
- xpath || expectList
- '/parent[@id=1]' || true
- '/parent[@id=1]/child' || false
- '/parent/child[@id=1]' || true
- '//child[@id=1]' || false
+ xpath || expectList
+ '/parent[@id=1]' || true
+ '/parent[@id=1]/child' || false
+ '/parent/child[@id=1]' || true
+ '//child[@id=1]' || false
+ '/parent/ancestor::grandparent[@id=1]' || false
}
def 'Parsing Exception'() {
diff --git a/cps-rest/docs/openapi/components.yml b/cps-rest/docs/openapi/components.yml
index 01375f503d..40f0e170ff 100644
--- a/cps-rest/docs/openapi/components.yml
+++ b/cps-rest/docs/openapi/components.yml
@@ -141,17 +141,17 @@ components:
name: kids
deltaReportSample:
value:
- - action: "ADD"
+ - action: "create"
xpath: "/bookstore/categories/[@code=3]"
target-data:
code: 3,
name: "kidz"
- - action: "REMOVE"
+ - action: "remove"
xpath: "/bookstore/categories/[@code=1]"
source-data:
code: 1,
name: "Fiction"
- - action: "UPDATE"
+ - action: "replace"
xpath: "/bookstore/categories/[@code=2]"
source-data:
name: "Funny"
@@ -183,6 +183,14 @@ components:
schema:
type: string
example: my-anchor
+ sourceAnchorNameInPath:
+ name: source-anchor-name
+ in: path
+ description: source-anchor-name
+ required: true
+ schema:
+ type: string
+ example: my-anchor
schemaSetNameInQuery:
name: schema-set-name
in: query
@@ -279,10 +287,10 @@ components:
type: string
enum: [v1, v2]
default: v2
- contentTypeHeader:
+ contentTypeInHeader:
name: Content-Type
in: header
- description: Content type header
+ description: Content type in header
schema:
type: string
example: 'application/json'
@@ -312,6 +320,15 @@ components:
schema:
type: integer
example: 10
+ dryRunInQuery:
+ name: dry-run
+ in: query
+ description: Boolean flag to validate data, without persisting it. Default value is set to false.
+ required: false
+ schema:
+ type: boolean
+ default: false
+ example: false
responses:
NotFound:
@@ -376,7 +393,7 @@ components:
Created:
description: Created
content:
- text/plain:
+ application/json:
schema:
type: string
example: my-resource
diff --git a/cps-rest/docs/openapi/cpsData.yml b/cps-rest/docs/openapi/cpsData.yml
index d13c285817..daf59bbfbf 100644
--- a/cps-rest/docs/openapi/cpsData.yml
+++ b/cps-rest/docs/openapi/cpsData.yml
@@ -32,15 +32,24 @@ listElementByDataspaceAndAnchor:
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
- $ref: 'components.yml#/components/parameters/requiredXpathInQuery'
- $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
+ - $ref: 'components.yml#/components/parameters/contentTypeInHeader'
requestBody:
required: true
content:
application/json:
schema:
- type: object
+ type: string
examples:
dataSample:
$ref: 'components.yml#/components/examples/dataSample'
+ application/xml:
+ schema:
+ type: object
+ xml:
+ name: stores
+ examples:
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSampleXml'
responses:
'201':
$ref: 'components.yml#/components/responses/Created'
@@ -93,8 +102,9 @@ nodesByDataspaceAndAnchor:
- $ref: 'components.yml#/components/parameters/dataspaceNameInPath'
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
- $ref: 'components.yml#/components/parameters/xpathInQuery'
+ - $ref: 'components.yml#/components/parameters/dryRunInQuery'
- $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
- - $ref: 'components.yml#/components/parameters/contentTypeHeader'
+ - $ref: 'components.yml#/components/parameters/contentTypeInHeader'
requestBody:
required: true
content:
@@ -112,7 +122,6 @@ nodesByDataspaceAndAnchor:
examples:
dataSample:
$ref: 'components.yml#/components/examples/dataSampleXml'
-
responses:
'201':
$ref: 'components.yml#/components/responses/Created'
@@ -137,7 +146,7 @@ nodesByDataspaceAndAnchor:
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
- $ref: 'components.yml#/components/parameters/xpathInQuery'
- $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
- - $ref: 'components.yml#/components/parameters/contentTypeHeader'
+ - $ref: 'components.yml#/components/parameters/contentTypeInHeader'
requestBody:
required: true
content:
@@ -197,15 +206,24 @@ nodesByDataspaceAndAnchor:
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
- $ref: 'components.yml#/components/parameters/xpathInQuery'
- $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
+ - $ref: 'components.yml#/components/parameters/contentTypeInHeader'
requestBody:
required: true
content:
application/json:
schema:
- type: object
+ type: string
examples:
dataSample:
$ref: 'components.yml#/components/examples/dataSample'
+ application/xml:
+ schema:
+ type: object
+ xml:
+ name: stores
+ examples:
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSampleXml'
responses:
'200':
$ref: 'components.yml#/components/responses/Ok'
diff --git a/cps-rest/docs/openapi/cpsDataV2.yml b/cps-rest/docs/openapi/cpsDataV2.yml
index a1433badf6..d5a8ef3891 100644
--- a/cps-rest/docs/openapi/cpsDataV2.yml
+++ b/cps-rest/docs/openapi/cpsDataV2.yml
@@ -46,7 +46,7 @@ nodeByDataspaceAndAnchor:
$ref: 'components.yml#/components/responses/InternalServerError'
x-codegen-request-body-name: xpath
-deltaByDataspaceAndAnchors:
+delta:
get:
description: Get delta between two anchors within a given dataspace
tags:
@@ -55,7 +55,7 @@ deltaByDataspaceAndAnchors:
operationId: getDeltaByDataspaceAndAnchors
parameters:
- $ref: 'components.yml#/components/parameters/dataspaceNameInPath'
- - $ref: 'components.yml#/components/parameters/anchorNameInPath'
+ - $ref: 'components.yml#/components/parameters/sourceAnchorNameInPath'
- $ref: 'components.yml#/components/parameters/targetAnchorNameInQuery'
- $ref: 'components.yml#/components/parameters/xpathInQuery'
- $ref: 'components.yml#/components/parameters/descendantsInQuery'
@@ -76,8 +76,6 @@ deltaByDataspaceAndAnchors:
'500':
$ref: 'components.yml#/components/responses/InternalServerError'
x-codegen-request-body-name: xpath
-
-deltaByDataspaceAnchorAndPayload:
post:
description: Get delta between an anchor in a dataspace and JSON payload
tags:
@@ -86,7 +84,7 @@ deltaByDataspaceAnchorAndPayload:
operationId: getDeltaByDataspaceAnchorAndPayload
parameters:
- $ref: 'components.yml#/components/parameters/dataspaceNameInPath'
- - $ref: 'components.yml#/components/parameters/anchorNameInPath'
+ - $ref: 'components.yml#/components/parameters/sourceAnchorNameInPath'
- $ref: 'components.yml#/components/parameters/xpathInQuery'
requestBody:
content:
diff --git a/cps-rest/docs/openapi/openapi.yml b/cps-rest/docs/openapi/openapi.yml
index b4e0b70408..95c32312cf 100644
--- a/cps-rest/docs/openapi/openapi.yml
+++ b/cps-rest/docs/openapi/openapi.yml
@@ -1,5 +1,5 @@
# ============LICENSE_START=======================================================
-# Copyright (C) 2021-2023 Nordix Foundation
+# Copyright (C) 2021-2024 Nordix Foundation
# Modifications Copyright (C) 2021 Pantheon.tech
# Modifications Copyright (C) 2021 Bell Canada.
# Modifications Copyright (C) 2022-2024 TechMahindra Ltd.
@@ -23,7 +23,7 @@ openapi: 3.0.3
info:
title: ONAP Open API v3 Configuration Persistence Service
description: Configuration Persistence Service is a Model Driven Generic Database
- version: "1.0.0"
+ version: "3.5.4"
contact:
name: ONAP
url: "https://onap.readthedocs.io"
@@ -31,10 +31,6 @@ info:
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0"
- x-planned-retirement-date: "202212"
- x-component: "Modeling"
- x-logo:
- url: "cps_logo.png"
servers:
- url: /cps/api
@@ -104,11 +100,8 @@ paths:
/{apiVersion}/dataspaces/{dataspace-name}/anchors/{anchor-name}/list-nodes:
$ref: 'cpsData.yml#/listElementByDataspaceAndAnchor'
- /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/deltaAnchors:
- $ref: 'cpsDataV2.yml#/deltaByDataspaceAndAnchors'
-
- /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/deltaPayload:
- $ref: 'cpsDataV2.yml#/deltaByDataspaceAnchorAndPayload'
+ /v2/dataspaces/{dataspace-name}/anchors/{source-anchor-name}/delta:
+ $ref: 'cpsDataV2.yml#/delta'
/v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/nodes/query:
$ref: 'cpsQueryV1Deprecated.yml#/nodesByDataspaceAndAnchorAndCpsPath'
diff --git a/cps-rest/pom.xml b/cps-rest/pom.xml
index f7ddd6c382..71395c2f0d 100644
--- a/cps-rest/pom.xml
+++ b/cps-rest/pom.xml
@@ -27,7 +27,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
@@ -196,6 +196,25 @@
</resources>
</configuration>
</execution>
+ <execution>
+ <id>copy-to-doc-folder</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.basedir}/../docs/api/swagger/cps</outputDirectory>
+ <overwrite>true</overwrite>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/target/generated-sources/openapi/</directory>
+ <includes>
+ <include>openapi.yaml</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
</executions>
</plugin>
</plugins>
diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
index f579c82d25..7390afcf98 100755
--- a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
+++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2020-2022 Bell Canada.
* Modifications Copyright (C) 2021 Pantheon.tech
- * Modifications Copyright (C) 2021-2023 Nordix Foundation
+ * Modifications Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2022-2024 TechMahindra Ltd.
* Modifications Copyright (C) 2022 Deutsche Telekom AG
* ================================================================================
@@ -37,9 +37,11 @@ import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.rest.api.CpsDataApi;
import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.model.Anchor;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.DeltaReport;
import org.onap.cps.utils.ContentType;
@@ -49,7 +51,6 @@ import org.onap.cps.utils.PrefixResolver;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@@ -64,25 +65,30 @@ public class DataRestController implements CpsDataApi {
private static final DateTimeFormatter ISO_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(ISO_TIMESTAMP_FORMAT);
private final CpsDataService cpsDataService;
+ private final CpsAnchorService cpsAnchorService;
private final JsonObjectMapper jsonObjectMapper;
private final PrefixResolver prefixResolver;
@Override
public ResponseEntity<String> createNode(final String apiVersion,
final String dataspaceName, final String anchorName,
- @RequestHeader(value = "Content-Type") final String contentTypeHeader,
+ final String contentTypeInHeader,
final String nodeData, final String parentNodeXpath,
- final String observedTimestamp) {
- final ContentType contentType = contentTypeHeader.contains(MediaType.APPLICATION_XML_VALUE) ? ContentType.XML
- : ContentType.JSON;
- if (isRootXpath(parentNodeXpath)) {
- cpsDataService.saveData(dataspaceName, anchorName, nodeData,
- toOffsetDateTime(observedTimestamp), contentType);
+ final Boolean dryRunEnabled, final String observedTimestamp) {
+ final ContentType contentType = getContentTypeFromHeader(contentTypeInHeader);
+ if (Boolean.TRUE.equals(dryRunEnabled)) {
+ cpsDataService.validateData(dataspaceName, anchorName, parentNodeXpath, nodeData, contentType);
+ return ResponseEntity.ok().build();
} else {
- cpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath,
- nodeData, toOffsetDateTime(observedTimestamp), contentType);
+ if (isRootXpath(parentNodeXpath)) {
+ cpsDataService.saveData(dataspaceName, anchorName, nodeData,
+ toOffsetDateTime(observedTimestamp), contentType);
+ } else {
+ cpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath,
+ nodeData, toOffsetDateTime(observedTimestamp), contentType);
+ }
+ return ResponseEntity.status(HttpStatus.CREATED).build();
}
- return new ResponseEntity<>(HttpStatus.CREATED);
}
@Override
@@ -97,9 +103,11 @@ public class DataRestController implements CpsDataApi {
@Override
public ResponseEntity<String> addListElements(final String apiVersion, final String dataspaceName,
final String anchorName, final String parentNodeXpath,
- final Object jsonData, final String observedTimestamp) {
+ final String contentTypeInHeader, final String nodeData,
+ final String observedTimestamp) {
+ final ContentType contentType = getContentTypeFromHeader(contentTypeInHeader);
cpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath,
- jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp));
+ nodeData, toOffsetDateTime(observedTimestamp), contentType);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@@ -112,7 +120,8 @@ public class DataRestController implements CpsDataApi {
? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS : FetchDescendantsOption.OMIT_DESCENDANTS;
final DataNode dataNode = cpsDataService.getDataNodes(dataspaceName, anchorName, xpath,
fetchDescendantsOption).iterator().next();
- final String prefix = prefixResolver.getPrefix(dataspaceName, anchorName, dataNode.getXpath());
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
+ final String prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath());
return new ResponseEntity<>(DataMapUtils.toDataMapWithIdentifier(dataNode, prefix), HttpStatus.OK);
}
@@ -127,8 +136,9 @@ public class DataRestController implements CpsDataApi {
final Collection<DataNode> dataNodes = cpsDataService.getDataNodes(dataspaceName, anchorName, xpath,
fetchDescendantsOption);
final List<Map<String, Object>> dataMaps = new ArrayList<>(dataNodes.size());
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
for (final DataNode dataNode: dataNodes) {
- final String prefix = prefixResolver.getPrefix(dataspaceName, anchorName, dataNode.getXpath());
+ final String prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath());
final Map<String, Object> dataMap = DataMapUtils.toDataMapWithIdentifier(dataNode, prefix);
dataMaps.add(dataMap);
}
@@ -137,23 +147,23 @@ public class DataRestController implements CpsDataApi {
@Override
public ResponseEntity<Object> updateNodeLeaves(final String apiVersion, final String dataspaceName,
- final String anchorName, final String contentTypeHeader,
+ final String anchorName, final String contentTypeInHeader,
final String nodeData, final String parentNodeXpath,
final String observedTimestamp) {
- final ContentType contentType = contentTypeHeader.contains(MediaType.APPLICATION_XML_VALUE) ? ContentType.XML
- : ContentType.JSON;
+ final ContentType contentType = getContentTypeFromHeader(contentTypeInHeader);
cpsDataService.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath,
nodeData, toOffsetDateTime(observedTimestamp), contentType);
return new ResponseEntity<>(HttpStatus.OK);
}
@Override
- public ResponseEntity<Object> replaceNode(final String apiVersion,
- final String dataspaceName, final String anchorName,
- final Object jsonData, final String parentNodeXpath, final String observedTimestamp) {
- cpsDataService
- .updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath,
- jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp));
+ public ResponseEntity<Object> replaceNode(final String apiVersion, final String dataspaceName,
+ final String anchorName, final String contentTypeInHeader,
+ final String nodeData, final String parentNodeXpath,
+ final String observedTimestamp) {
+ final ContentType contentType = getContentTypeFromHeader(contentTypeInHeader);
+ cpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath,
+ nodeData, toOffsetDateTime(observedTimestamp), contentType);
return new ResponseEntity<>(HttpStatus.OK);
}
@@ -213,6 +223,10 @@ public class DataRestController implements CpsDataApi {
return new ResponseEntity<>(jsonObjectMapper.asJsonString(deltaBetweenAnchors), HttpStatus.OK);
}
+ private static ContentType getContentTypeFromHeader(final String contentTypeInHeader) {
+ return contentTypeInHeader.contains(MediaType.APPLICATION_XML_VALUE) ? ContentType.XML : ContentType.JSON;
+ }
+
private static boolean isRootXpath(final String xpath) {
return ROOT_XPATH.equals(xpath);
}
diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java
index 5334b48143..547be669ae 100644
--- a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java
+++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada.
* Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
* ================================================================================
@@ -29,10 +29,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsQueryService;
import org.onap.cps.rest.api.CpsQueryApi;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.PaginationOption;
+import org.onap.cps.spi.model.Anchor;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.utils.DataMapUtils;
import org.onap.cps.utils.JsonObjectMapper;
@@ -48,6 +50,7 @@ import org.springframework.web.bind.annotation.RestController;
public class QueryRestController implements CpsQueryApi {
private final CpsQueryService cpsQueryService;
+ private final CpsAnchorService cpsAnchorService;
private final JsonObjectMapper jsonObjectMapper;
private final PrefixResolver prefixResolver;
@@ -87,14 +90,15 @@ public class QueryRestController implements CpsQueryApi {
cpsPath, fetchDescendantsOption, paginationOption);
final List<Map<String, Object>> dataNodesAsListOfMaps = new ArrayList<>(dataNodes.size());
String prefix = null;
- final Map<String, List<DataNode>> anchorDataNodeListMap = prepareDataNodesForAnchor(dataNodes);
- for (final Map.Entry<String, List<DataNode>> anchorDataNodesMapEntry : anchorDataNodeListMap.entrySet()) {
+ final Map<String, List<DataNode>> dataNodesPerAnchor = groupDataNodesPerAnchor(dataNodes);
+ for (final Map.Entry<String, List<DataNode>> dataNodesPerAnchorEntry : dataNodesPerAnchor.entrySet()) {
+ final String anchorName = dataNodesPerAnchorEntry.getKey();
if (prefix == null) {
- prefix = prefixResolver.getPrefix(dataspaceName, anchorDataNodesMapEntry.getKey(),
- anchorDataNodesMapEntry.getValue().get(0).getXpath());
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
+ prefix = prefixResolver.getPrefix(anchor, dataNodesPerAnchorEntry.getValue().get(0).getXpath());
}
final Map<String, Object> dataMap = DataMapUtils.toDataMapWithIdentifierAndAnchor(
- anchorDataNodesMapEntry.getValue(), anchorDataNodesMapEntry.getKey(), prefix);
+ dataNodesPerAnchorEntry.getValue(), anchorName, prefix);
dataNodesAsListOfMaps.add(dataMap);
}
final Integer totalPages = getTotalPages(dataspaceName, cpsPath, paginationOption);
@@ -112,7 +116,7 @@ public class QueryRestController implements CpsQueryApi {
: (int) Math.ceil((double) totalAnchors / paginationOption.getPageSize());
}
- private Map<String, List<DataNode>> prepareDataNodesForAnchor(final Collection<DataNode> dataNodes) {
+ private Map<String, List<DataNode>> groupDataNodesPerAnchor(final Collection<DataNode> dataNodes) {
final Map<String, List<DataNode>> dataNodesMapForAnchor = new HashMap<>();
for (final DataNode dataNode : dataNodes) {
List<DataNode> dataNodesInAnchor = dataNodesMapForAnchor.get(dataNode.getAnchorName());
@@ -130,10 +134,11 @@ public class QueryRestController implements CpsQueryApi {
final Collection<DataNode> dataNodes =
cpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption);
final List<Map<String, Object>> dataNodesAsListOfMaps = new ArrayList<>(dataNodes.size());
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
String prefix = null;
for (final DataNode dataNode : dataNodes) {
if (prefix == null) {
- prefix = prefixResolver.getPrefix(dataspaceName, anchorName, dataNode.getXpath());
+ prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath());
}
final Map<String, Object> dataMap = DataMapUtils.toDataMapWithIdentifier(dataNode, prefix);
dataNodesAsListOfMaps.add(dataMap);
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
index 317b9c5b7c..705c2fee91 100755
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2021-2022 Bell Canada.
* Modifications Copyright (C) 2022 Deutsche Telekom AG
@@ -26,6 +26,7 @@ package org.onap.cps.rest.controller
import com.fasterxml.jackson.databind.ObjectMapper
import groovy.json.JsonSlurper
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDataService
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.model.DataNode
@@ -63,6 +64,9 @@ class DataRestControllerSpec extends Specification {
CpsDataService mockCpsDataService = Mock()
@SpringBean
+ CpsAnchorService mockCpsAnchorService = Mock()
+
+ @SpringBean
JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
@SpringBean
@@ -160,6 +164,26 @@ class DataRestControllerSpec extends Specification {
'with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_JSON | requestBodyJson || 0 | HttpStatus.BAD_REQUEST | expectedJsonData | ContentType.JSON
}
+ def 'Validate data using create a node API'() {
+ given: 'an endpoint to create a node'
+ def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/nodes"
+ def parentNodeXpath = '/'
+ def dryRunEnabled = 'true'
+ when: 'post is invoked with json data and dry-run flag enabled'
+ def response =
+ mvc.perform(
+ post(endpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .param('xpath', parentNodeXpath)
+ .param('dry-run', dryRunEnabled)
+ .content(requestBodyJson)
+ ).andReturn().response
+ then: 'a 200 OK response is returned'
+ response.status == HttpStatus.OK.value()
+ then: 'the service was called with correct parameters'
+ 1 * mockCpsDataService.validateData(dataspaceName, anchorName, parentNodeXpath, requestBodyJson, ContentType.JSON)
+ }
+
def 'Create a child node #scenario'() {
given: 'endpoint to create a node'
def endpoint = "$dataNodeBaseEndpointV1/anchors/$anchorName/nodes"
@@ -192,22 +216,25 @@ class DataRestControllerSpec extends Specification {
def rootNodeXpath = '/'
when: 'list-node endpoint is invoked with post (create) operation'
def postRequestBuilder = post("$dataNodeBaseEndpointV1/anchors/$anchorName/list-nodes")
- .contentType(MediaType.APPLICATION_JSON)
+ .contentType(contentType)
.param('xpath', rootNodeXpath )
- .content(requestBodyJson)
+ .content(requestBody)
if (observedTimestamp != null)
postRequestBuilder.param('observed-timestamp', observedTimestamp)
def response = mvc.perform(postRequestBuilder).andReturn().response
then: 'a created response is returned'
response.status == expectedHttpStatus.value()
then: 'the java API was called with the correct parameters'
- expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, rootNodeXpath, expectedJsonData,
- { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
+ expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, rootNodeXpath, expectedData,
+ { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, expectedContentType)
where:
- scenario | observedTimestamp || expectedApiCount | expectedHttpStatus
- 'with observed-timestamp' | '2021-03-03T23:59:59.999-0400' || 1 | HttpStatus.CREATED
- 'without observed-timestamp' | null || 1 | HttpStatus.CREATED
- 'with invalid observed-timestamp' | 'invalid' || 0 | HttpStatus.BAD_REQUEST
+ scenario | observedTimestamp | contentType | requestBody || expectedApiCount | expectedHttpStatus | expectedData | expectedContentType
+ 'Content type JSON with observed-timestamp' | '2021-03-03T23:59:59.999-0400' | MediaType.APPLICATION_JSON | requestBodyJson || 1 | HttpStatus.CREATED | expectedJsonData | ContentType.JSON
+ 'Content type JSON without observed-timestamp' | null | MediaType.APPLICATION_JSON | requestBodyJson || 1 | HttpStatus.CREATED | expectedJsonData | ContentType.JSON
+ 'Content type JSON with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_JSON | requestBodyJson || 0 | HttpStatus.BAD_REQUEST | expectedJsonData | ContentType.JSON
+ 'Content type XML with observed-timestamp' | '2021-03-03T23:59:59.999-0400' | MediaType.APPLICATION_XML | requestBodyXml || 1 | HttpStatus.CREATED | expectedXmlData | ContentType.XML
+ 'Content type XML without observed-timestamp' | null | MediaType.APPLICATION_XML | requestBodyXml || 1 | HttpStatus.CREATED | expectedXmlData | ContentType.XML
+ 'Content type XML with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_XML | requestBodyXml || 0 | HttpStatus.BAD_REQUEST | expectedXmlData | ContentType.XML
}
def 'Save list elements #scenario.'() {
@@ -215,22 +242,25 @@ class DataRestControllerSpec extends Specification {
def parentNodeXpath = 'parent node xpath'
when: 'list-node endpoint is invoked with post (create) operation'
def postRequestBuilder = post("$dataNodeBaseEndpointV1/anchors/$anchorName/list-nodes")
- .contentType(MediaType.APPLICATION_JSON)
+ .contentType(contentType)
.param('xpath', parentNodeXpath)
- .content(requestBodyJson)
+ .content(requestBody)
if (observedTimestamp != null)
postRequestBuilder.param('observed-timestamp', observedTimestamp)
def response = mvc.perform(postRequestBuilder).andReturn().response
then: 'a created response is returned'
response.status == expectedHttpStatus.value()
then: 'the java API was called with the correct parameters'
- expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, expectedJsonData,
- { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
+ expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, expectedData,
+ { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, expectedContentType)
where:
- scenario | observedTimestamp || expectedApiCount | expectedHttpStatus
- 'with observed-timestamp' | '2021-03-03T23:59:59.999-0400' || 1 | HttpStatus.CREATED
- 'without observed-timestamp' | null || 1 | HttpStatus.CREATED
- 'with invalid observed-timestamp' | 'invalid' || 0 | HttpStatus.BAD_REQUEST
+ scenario | observedTimestamp | contentType | requestBody || expectedApiCount | expectedHttpStatus | expectedData | expectedContentType
+ 'Content type JSON with observed-timestamp' | '2021-03-03T23:59:59.999-0400' | MediaType.APPLICATION_JSON | requestBodyJson || 1 | HttpStatus.CREATED | expectedJsonData | ContentType.JSON
+ 'Content type JSON without observed-timestamp' | null | MediaType.APPLICATION_JSON | requestBodyJson || 1 | HttpStatus.CREATED | expectedJsonData | ContentType.JSON
+ 'Content type JSON with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_JSON | requestBodyJson || 0 | HttpStatus.BAD_REQUEST | expectedJsonData | ContentType.JSON
+ 'Content type XML with observed-timestamp' | '2021-03-03T23:59:59.999-0400' | MediaType.APPLICATION_XML | requestBodyXml || 1 | HttpStatus.CREATED | expectedXmlData | ContentType.XML
+ 'Content type XML without observed-timestamp' | null | MediaType.APPLICATION_XML | requestBodyXml || 1 | HttpStatus.CREATED | expectedXmlData | ContentType.XML
+ 'Content type XML with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_XML | requestBodyXml || 0 | HttpStatus.BAD_REQUEST | expectedXmlData | ContentType.XML
}
def 'Get data node with leaves'() {
@@ -344,9 +374,9 @@ class DataRestControllerSpec extends Specification {
def 'Get delta between two anchors'() {
given: 'the service returns a list containing delta reports'
- def deltaReports = new DeltaReportBuilder().actionUpdate().withXpath('some xpath').withSourceData('some key': 'some value').withTargetData('some key': 'some value').build()
+ def deltaReports = new DeltaReportBuilder().actionReplace().withXpath('some xpath').withSourceData('some key': 'some value').withTargetData('some key': 'some value').build()
def xpath = 'some xpath'
- def endpoint = "$dataNodeBaseEndpointV2/anchors/sourceAnchor/deltaAnchors"
+ def endpoint = "$dataNodeBaseEndpointV2/anchors/sourceAnchor/delta"
mockCpsDataService.getDeltaByDataspaceAndAnchors(dataspaceName, 'sourceAnchor', 'targetAnchor', xpath, OMIT_DESCENDANTS) >> [deltaReports]
when: 'get delta request is performed using REST API'
def response =
@@ -357,14 +387,14 @@ class DataRestControllerSpec extends Specification {
then: 'expected response code is returned'
assert response.status == HttpStatus.OK.value()
and: 'the response contains expected value'
- assert response.contentAsString.contains("[{\"action\":\"update\",\"xpath\":\"some xpath\",\"sourceData\":{\"some key\":\"some value\"},\"targetData\":{\"some key\":\"some value\"}}]")
+ assert response.contentAsString.contains("[{\"action\":\"replace\",\"xpath\":\"some xpath\",\"sourceData\":{\"some key\":\"some value\"},\"targetData\":{\"some key\":\"some value\"}}]")
}
def 'Get delta between anchor and JSON payload with multipart file'() {
given: 'sample delta report, xpath, yang model file and json payload'
- def deltaReports = new DeltaReportBuilder().actionAdd().withXpath('some xpath').build()
+ def deltaReports = new DeltaReportBuilder().actionCreate().withXpath('some xpath').build()
def xpath = 'some xpath'
- def endpoint = "$dataNodeBaseEndpointV2/anchors/$anchorName/deltaPayload"
+ def endpoint = "$dataNodeBaseEndpointV2/anchors/$anchorName/delta"
and: 'the service layer returns a list containing delta reports'
mockCpsDataService.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, ['filename.yang':'content'], expectedJsonData, INCLUDE_ALL_DESCENDANTS) >> [deltaReports]
when: 'get delta request is performed using REST API'
@@ -378,14 +408,14 @@ class DataRestControllerSpec extends Specification {
then: 'expected response code is returned'
assert response.status == HttpStatus.OK.value()
and: 'the response contains expected value'
- assert response.contentAsString.contains("[{\"action\":\"add\",\"xpath\":\"some xpath\"}]")
+ assert response.contentAsString.contains("[{\"action\":\"create\",\"xpath\":\"some xpath\"}]")
}
def 'Get delta between anchor and JSON payload without multipart file'() {
given: 'sample delta report, xpath, and json payload'
def deltaReports = new DeltaReportBuilder().actionRemove().withXpath('some xpath').build()
def xpath = 'some xpath'
- def endpoint = "$dataNodeBaseEndpointV2/anchors/$anchorName/deltaPayload"
+ def endpoint = "$dataNodeBaseEndpointV2/anchors/$anchorName/delta"
and: 'the service layer returns a list containing delta reports'
mockCpsDataService.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, [:], expectedJsonData, INCLUDE_ALL_DESCENDANTS) >> [deltaReports]
when: 'get delta request is performed using REST API'
@@ -456,19 +486,22 @@ class DataRestControllerSpec extends Specification {
def response =
mvc.perform(
put(endpoint)
- .contentType(MediaType.APPLICATION_JSON)
- .content(requestBodyJson)
+ .contentType(contentType)
+ .content(requestBody)
.param('xpath', inputXpath))
.andReturn().response
then: 'the service method is invoked with expected parameters'
- 1 * mockCpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, xpathServiceParameter, expectedJsonData, noTimestamp)
+ 1 * mockCpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, xpathServiceParameter, expectedData, noTimestamp, expectedContentType)
and: 'response status indicates success'
response.status == HttpStatus.OK.value()
where:
- scenario | inputXpath || xpathServiceParameter
- 'root node by default' | '' || '/'
- 'root node by choice' | '/' || '/'
- 'some xpath by parent' | '/some/xpath' || '/some/xpath'
+ scenario | inputXpath | contentType || xpathServiceParameter | requestBody | expectedData | expectedContentType
+ 'JSON content: root node by default' | '' | MediaType.APPLICATION_JSON || '/' | requestBodyJson | expectedJsonData | ContentType.JSON
+ 'JSON content: root node by choice' | '/' | MediaType.APPLICATION_JSON || '/' | requestBodyJson | expectedJsonData | ContentType.JSON
+ 'JSON content: some xpath by parent' | '/some/xpath' | MediaType.APPLICATION_JSON || '/some/xpath' | requestBodyJson | expectedJsonData | ContentType.JSON
+ 'XML content: root node by default' | '' | MediaType.APPLICATION_XML || '/' | requestBodyXml | expectedXmlData | ContentType.XML
+ 'XML content: root node by choice' | '/' | MediaType.APPLICATION_XML || '/' | requestBodyXml | expectedXmlData | ContentType.XML
+ 'XML content: some xpath by parent' | '/some/xpath' | MediaType.APPLICATION_XML || '/some/xpath' | requestBodyXml | expectedXmlData | ContentType.XML
}
def 'Update data node and descendants with observedTimestamp.'() {
@@ -485,7 +518,7 @@ class DataRestControllerSpec extends Specification {
.andReturn().response
then: 'the service method is invoked with expected parameters'
expectedApiCount * mockCpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, '/', expectedJsonData,
- { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
+ { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, ContentType.JSON)
and: 'response status indicates success'
response.status == expectedHttpStatus.value()
where:
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
index c30a63fd46..80b287cda8 100644
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2021-2022 Bell Canada.
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
@@ -23,18 +23,13 @@
package org.onap.cps.rest.controller
-import org.onap.cps.spi.PaginationOption
-import org.onap.cps.utils.PrefixResolver
-
-import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
-
import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.utils.JsonObjectMapper
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsQueryService
+import org.onap.cps.spi.PaginationOption
import org.onap.cps.spi.model.DataNodeBuilder
+import org.onap.cps.utils.JsonObjectMapper
+import org.onap.cps.utils.PrefixResolver
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
@@ -43,6 +38,11 @@ import org.springframework.http.HttpStatus
import org.springframework.test.web.servlet.MockMvc
import spock.lang.Specification
+import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+
@WebMvcTest(QueryRestController)
class QueryRestControllerSpec extends Specification {
@@ -50,6 +50,9 @@ class QueryRestControllerSpec extends Specification {
CpsQueryService mockCpsQueryService = Mock()
@SpringBean
+ CpsAnchorService mockCpsAnchorService = Mock()
+
+ @SpringBean
JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
@SpringBean
diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml
index 14a091fcbd..1ab5920ef4 100644
--- a/cps-ri/pom.xml
+++ b/cps-ri/pom.xml
@@ -26,7 +26,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
@@ -68,6 +68,11 @@
<artifactId>postgresql</artifactId>
<version>${postgres.version}</version>
</dependency>
+ <!-- Disable SpotBug Rules -->
+ <dependency>
+ <groupId>com.github.spotbugs</groupId>
+ <artifactId>spotbugs-annotations</artifactId>
+ </dependency>
<!-- Add Hibernate support for Postgres datatype JSONB and Postgres arrays -->
<dependency>
<groupId>io.hypersistence</groupId>
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsAdminPersistenceServiceImpl.java
index 13710dbec0..b85b0f9b98 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsAdminPersistenceServiceImpl.java
@@ -21,24 +21,24 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.impl;
+package org.onap.cps.ri;
import jakarta.transaction.Transactional;
import java.util.Collection;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.SchemaSetEntity;
+import org.onap.cps.ri.repository.AnchorRepository;
+import org.onap.cps.ri.repository.DataspaceRepository;
+import org.onap.cps.ri.repository.SchemaSetRepository;
import org.onap.cps.spi.CpsAdminPersistenceService;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.SchemaSetEntity;
import org.onap.cps.spi.exceptions.AlreadyDefinedException;
import org.onap.cps.spi.exceptions.DataspaceInUseException;
import org.onap.cps.spi.model.Anchor;
import org.onap.cps.spi.model.Dataspace;
-import org.onap.cps.spi.repository.AnchorRepository;
-import org.onap.cps.spi.repository.DataspaceRepository;
-import org.onap.cps.spi.repository.SchemaSetRepository;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Component;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java
index fd47793a7a..ecbe447226 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java
@@ -21,7 +21,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.impl;
+package org.onap.cps.ri;
import static org.onap.cps.spi.PaginationOption.NO_PAGINATION;
@@ -48,12 +48,16 @@ import org.hibernate.StaleStateException;
import org.onap.cps.cpspath.parser.CpsPathQuery;
import org.onap.cps.cpspath.parser.CpsPathUtil;
import org.onap.cps.cpspath.parser.PathParsingException;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.FragmentEntity;
+import org.onap.cps.ri.repository.AnchorRepository;
+import org.onap.cps.ri.repository.DataspaceRepository;
+import org.onap.cps.ri.repository.FragmentRepository;
+import org.onap.cps.ri.utils.SessionManager;
import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.PaginationOption;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.FragmentEntity;
import org.onap.cps.spi.exceptions.AlreadyDefinedException;
import org.onap.cps.spi.exceptions.ConcurrencyException;
import org.onap.cps.spi.exceptions.CpsAdminException;
@@ -62,10 +66,6 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
import org.onap.cps.spi.exceptions.DataNodeNotFoundExceptionBatch;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.DataNodeBuilder;
-import org.onap.cps.spi.repository.AnchorRepository;
-import org.onap.cps.spi.repository.DataspaceRepository;
-import org.onap.cps.spi.repository.FragmentRepository;
-import org.onap.cps.spi.utils.SessionManager;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
@@ -228,6 +228,9 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
final Collection<String> xpaths = xpathToUpdatedDataNode.keySet();
Collection<FragmentEntity> existingFragmentEntities = getFragmentEntities(anchorEntity, xpaths);
+
+ logMissingXPaths(xpaths, existingFragmentEntities);
+
existingFragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(
FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS, existingFragmentEntities);
@@ -243,6 +246,19 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
}
}
+ private void logMissingXPaths(final Collection<String> xpaths, final Collection<FragmentEntity>
+ existingFragmentEntities) {
+ final Set<String> existingXPaths = existingFragmentEntities.stream().map(FragmentEntity::getXpath)
+ .collect(Collectors.toSet());
+
+ final Set<String> missingXPaths = xpaths.stream().filter(xpath -> !existingXPaths.contains(xpath))
+ .collect(Collectors.toSet());
+
+ if (!missingXPaths.isEmpty()) {
+ log.warn("Cannot update data nodes: Target XPaths {} not found in DB.", missingXPaths);
+ }
+ }
+
private void retryUpdateDataNodesIndividually(final AnchorEntity anchorEntity,
final Collection<FragmentEntity> fragmentEntities) {
final Collection<String> failedXpaths = new HashSet<>();
@@ -341,8 +357,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
if (anchorIds.isEmpty()) {
fragmentEntities = fragmentRepository.findByDataspaceAndXpathIn(dataspaceEntity, ancestorXpaths);
} else {
- fragmentEntities = fragmentRepository.findByAnchorIdsAndXpathIn(
- anchorIds.toArray(new Long[0]), ancestorXpaths.toArray(new String[0]));
+ fragmentEntities = fragmentRepository.findByAnchorIdsAndXpathIn(anchorIds, ancestorXpaths);
}
}
@@ -475,7 +490,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
fragmentRepository.findAllXpathByAnchorAndXpathIn(anchorEntity, deleteChecklist);
if (onlySupportListDeletion) {
final Collection<String> xpathsToExistingListElements = xpathsToExistingContainers.stream()
- .filter(CpsPathUtil::isPathToListElement).collect(Collectors.toList());
+ .filter(CpsPathUtil::isPathToListElement).toList();
deleteChecklist.removeAll(xpathsToExistingListElements);
} else {
deleteChecklist.removeAll(xpathsToExistingContainers);
@@ -483,15 +498,19 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
final Collection<String> xpathsToExistingLists = deleteChecklist.stream()
.filter(xpath -> fragmentRepository.existsByAnchorAndXpathStartsWith(anchorEntity, xpath + "["))
- .collect(Collectors.toList());
+ .toList();
deleteChecklist.removeAll(xpathsToExistingLists);
if (!deleteChecklist.isEmpty()) {
throw new DataNodeNotFoundExceptionBatch(dataspaceName, anchorName, deleteChecklist);
}
- fragmentRepository.deleteByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingContainers);
- fragmentRepository.deleteListsByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingLists);
+ if (!xpathsToExistingContainers.isEmpty()) {
+ fragmentRepository.deleteByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingContainers);
+ }
+ for (final String listXpath : xpathsToExistingLists) {
+ fragmentRepository.deleteListByAnchorIdAndXpath(anchorEntity.getId(), listXpath);
+ }
}
@Override
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java
index 17f13b81ad..6f491ba3b7 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java
@@ -21,7 +21,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.impl;
+package org.onap.cps.ri;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -48,21 +48,21 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.exception.ConstraintViolationException;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.SchemaSetEntity;
+import org.onap.cps.ri.models.YangResourceEntity;
+import org.onap.cps.ri.models.YangResourceModuleReference;
+import org.onap.cps.ri.repository.DataspaceRepository;
+import org.onap.cps.ri.repository.ModuleReferenceRepository;
+import org.onap.cps.ri.repository.SchemaSetRepository;
+import org.onap.cps.ri.repository.YangResourceRepository;
import org.onap.cps.spi.CpsModulePersistenceService;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.SchemaSetEntity;
-import org.onap.cps.spi.entities.YangResourceEntity;
-import org.onap.cps.spi.entities.YangResourceModuleReference;
import org.onap.cps.spi.exceptions.AlreadyDefinedException;
import org.onap.cps.spi.exceptions.DuplicatedYangResourceException;
import org.onap.cps.spi.exceptions.ModelValidationException;
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.DataspaceRepository;
-import org.onap.cps.spi.repository.ModuleReferenceRepository;
-import org.onap.cps.spi.repository.SchemaSetRepository;
-import org.onap.cps.spi.repository.YangResourceRepository;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
@@ -241,6 +241,15 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
return moduleReferenceRepository.identifyNewModuleReferences(moduleReferencesToCheck);
}
+ @Override
+ public Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName,
+ final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes) {
+ return moduleReferenceRepository.findModuleReferences(dataspaceName, anchorName, parentAttributes,
+ childAttributes);
+ }
+
private Set<YangResourceEntity> synchronizeYangResources(
final Map<String, String> moduleReferenceNameToContentMap) {
final Map<String, YangResourceEntity> checksumToEntityMap = moduleReferenceNameToContentMap.entrySet().stream()
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/AnchorEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/AnchorEntity.java
index ac06b0b5a4..bf9e25daf1 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/entities/AnchorEntity.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/models/AnchorEntity.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.entities;
+package org.onap.cps.ri.models;
import jakarta.persistence.Column;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/DataspaceEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/DataspaceEntity.java
index ddfb09c942..689ae2c000 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/entities/DataspaceEntity.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/models/DataspaceEntity.java
@@ -19,7 +19,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.entities;
+package org.onap.cps.ri.models;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/FragmentEntity.java
index c763f61f8f..2c851b60a5 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/models/FragmentEntity.java
@@ -19,7 +19,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.entities;
+package org.onap.cps.ri.models;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/SchemaSetEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/SchemaSetEntity.java
index e07f766ed0..e99f79e330 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/entities/SchemaSetEntity.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/models/SchemaSetEntity.java
@@ -17,7 +17,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.entities;
+package org.onap.cps.ri.models;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceEntity.java
index 0c54baa4df..2b2d7924db 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceEntity.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceEntity.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.entities;
+package org.onap.cps.ri.models;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceModuleReference.java b/cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceModuleReference.java
index 3c39c6baac..28ef56d38a 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceModuleReference.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceModuleReference.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.entities;
+package org.onap.cps.ri.models;
import org.springframework.beans.factory.annotation.Value;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java
index d78a016c2e..f7f750c983 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java
@@ -1,7 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2021 Pantheon.tech
- * Modifications Copyright (C) 2021-2023 Nordix Foundation
+ * Modifications Copyright (C) 2021-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,13 +18,13 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.util.Collection;
import java.util.Optional;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.SchemaSetEntity;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.SchemaSetEntity;
import org.onap.cps.spi.exceptions.AnchorNotFoundException;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
@@ -46,26 +46,26 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Long> {
Collection<AnchorEntity> findAllBySchemaSet(SchemaSetEntity schemaSetEntity);
- @Query(value = "SELECT * FROM anchor WHERE dataspace_id = :dataspaceId AND name = ANY (:anchorNames)",
+ @Query(value = "SELECT * FROM anchor WHERE dataspace_id = :dataspaceId AND name IN (:anchorNames)",
nativeQuery = true)
Collection<AnchorEntity> findAllByDataspaceIdAndNameIn(@Param("dataspaceId") int dataspaceId,
- @Param("anchorNames") String[] anchorNames);
+ @Param("anchorNames") Collection<String> anchorNames);
default Collection<AnchorEntity> findAllByDataspaceAndNameIn(final DataspaceEntity dataspaceEntity,
final Collection<String> anchorNames) {
- return findAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames.toArray(new String[0]));
+ return findAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames);
}
@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)",
+ + " WHERE a.dataspace_id = :dataspaceId AND s.name IN (:schemaSetNames)",
nativeQuery = true)
- Collection<AnchorEntity> findAllByDataspaceIdAndSchemaSetNameIn(@Param("dataspaceId") int dataspaceId,
- @Param("schemaSetNames") String[] schemaSetNames);
+ Collection<AnchorEntity> findAllByDataspaceIdAndSchemaSetNameIn(
+ @Param("dataspaceId") int dataspaceId, @Param("schemaSetNames") Collection<String> schemaSetNames);
default Collection<AnchorEntity> findAllByDataspaceAndSchemaSetNameIn(final DataspaceEntity dataspaceEntity,
final Collection<String> schemaSetNames) {
- return findAllByDataspaceIdAndSchemaSetNameIn(dataspaceEntity.getId(), schemaSetNames.toArray(new String[0]));
+ return findAllByDataspaceIdAndSchemaSetNameIn(dataspaceEntity.getId(), schemaSetNames);
}
Integer countByDataspace(DataspaceEntity dataspaceEntity);
@@ -80,7 +80,7 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Long> {
JOIN anchor ON anchor.schema_set_id = schema_set.id
WHERE
schema_set.dataspace_id = :dataspaceId
- AND module_name = ANY ( :moduleNames )
+ AND module_name IN (:moduleNames)
GROUP BY
anchor.id,
anchor.name,
@@ -90,25 +90,18 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Long> {
COUNT(DISTINCT module_name) = :sizeOfModuleNames
""", nativeQuery = true)
Collection<String> getAnchorNamesByDataspaceIdAndModuleNames(@Param("dataspaceId") int dataspaceId,
- @Param("moduleNames") String[] moduleNames,
+ @Param("moduleNames") Collection<String> moduleNames,
@Param("sizeOfModuleNames") int sizeOfModuleNames);
- default Collection<String> getAnchorNamesByDataspaceIdAndModuleNames(final int dataspaceId,
- final Collection<String> moduleNames,
- final int sizeOfModuleNames) {
- final String[] moduleNamesArray = moduleNames.toArray(new String[0]);
- return getAnchorNamesByDataspaceIdAndModuleNames(dataspaceId, moduleNamesArray, sizeOfModuleNames);
- }
-
@Modifying
- @Query(value = "DELETE FROM anchor WHERE dataspace_id = :dataspaceId AND name = ANY (:anchorNames)",
+ @Query(value = "DELETE FROM anchor WHERE dataspace_id = :dataspaceId AND name IN (:anchorNames)",
nativeQuery = true)
void deleteAllByDataspaceIdAndNameIn(@Param("dataspaceId") int dataspaceId,
- @Param("anchorNames") String[] anchorNames);
+ @Param("anchorNames") Collection<String> anchorNames);
default void deleteAllByDataspaceAndNameIn(final DataspaceEntity dataspaceEntity,
final Collection<String> anchorNames) {
- deleteAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames.toArray(new String[0]));
+ deleteAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames);
}
@Modifying
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/DataspaceRepository.java
index b1ce127c4a..b79d802d95 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/DataspaceRepository.java
@@ -18,10 +18,10 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.util.Optional;
-import org.onap.cps.spi.entities.DataspaceEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
import org.onap.cps.spi.exceptions.DataspaceNotFoundException;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepository.java
index 2460db869a..6813995d99 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepository.java
@@ -18,11 +18,11 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.util.Collection;
+import org.onap.cps.ri.models.FragmentEntity;
import org.onap.cps.spi.FetchDescendantsOption;
-import org.onap.cps.spi.entities.FragmentEntity;
public interface FragmentPrefetchRepository {
Collection<FragmentEntity> prefetchDescendantsOfFragmentEntities(
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepositoryImpl.java
index c187f20ea9..bcf01b3d76 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepositoryImpl.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.sql.Connection;
import java.util.Collection;
@@ -29,9 +29,9 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.FragmentEntity;
import org.onap.cps.spi.FetchDescendantsOption;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.FragmentEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java
index b2014757d7..b8bbf59c23 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java
@@ -19,34 +19,30 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;
-import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
import org.onap.cps.cpspath.parser.CpsPathPrefixType;
import org.onap.cps.cpspath.parser.CpsPathQuery;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.FragmentEntity;
+import org.onap.cps.ri.utils.EscapeUtils;
import org.onap.cps.spi.PaginationOption;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.FragmentEntity;
import org.onap.cps.spi.exceptions.CpsPathException;
-import org.onap.cps.spi.utils.EscapeUtils;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
-@Slf4j
@Component
public class FragmentQueryBuilder {
- private static final AnchorEntity ACROSS_ALL_ANCHORS = null;
@PersistenceContext
private EntityManager entityManager;
@@ -59,8 +55,14 @@ public class FragmentQueryBuilder {
* @return a executable query object
*/
public Query getQueryForAnchorAndCpsPath(final AnchorEntity anchorEntity, final CpsPathQuery cpsPathQuery) {
- return getQueryForDataspaceOrAnchorAndCpsPath(anchorEntity.getDataspace(),
- anchorEntity, cpsPathQuery, Collections.emptyList());
+ final StringBuilder sqlStringBuilder = new StringBuilder();
+ final Map<String, Object> queryParameters = new HashMap<>();
+
+ sqlStringBuilder.append("SELECT fragment.* FROM fragment");
+ addWhereClauseForAnchor(anchorEntity, sqlStringBuilder, queryParameters);
+ addNodeSearchConditions(cpsPathQuery, sqlStringBuilder, queryParameters, false);
+
+ return getQuery(sqlStringBuilder.toString(), queryParameters, FragmentEntity.class);
}
/**
@@ -73,8 +75,18 @@ public class FragmentQueryBuilder {
public Query getQueryForDataspaceAndCpsPath(final DataspaceEntity dataspaceEntity,
final CpsPathQuery cpsPathQuery,
final List<Long> anchorIdsForPagination) {
- return getQueryForDataspaceOrAnchorAndCpsPath(dataspaceEntity, ACROSS_ALL_ANCHORS,
- cpsPathQuery, anchorIdsForPagination);
+ final StringBuilder sqlStringBuilder = new StringBuilder();
+ final Map<String, Object> queryParameters = new HashMap<>();
+
+ sqlStringBuilder.append("SELECT fragment.* FROM fragment");
+ if (anchorIdsForPagination.isEmpty()) {
+ addWhereClauseForDataspace(dataspaceEntity, sqlStringBuilder, queryParameters);
+ } else {
+ addWhereClauseForAnchorIds(anchorIdsForPagination, sqlStringBuilder, queryParameters);
+ }
+ addNodeSearchConditions(cpsPathQuery, sqlStringBuilder, queryParameters, true);
+
+ return getQuery(sqlStringBuilder.toString(), queryParameters, FragmentEntity.class);
}
/**
@@ -89,52 +101,52 @@ public class FragmentQueryBuilder {
final PaginationOption paginationOption) {
final StringBuilder sqlStringBuilder = new StringBuilder();
final Map<String, Object> queryParameters = new HashMap<>();
- sqlStringBuilder.append("SELECT distinct(fragment.anchor_id) FROM fragment "
- + "JOIN anchor ON anchor.id = fragment.anchor_id WHERE dataspace_id = :dataspaceId");
- queryParameters.put("dataspaceId", dataspaceEntity.getId());
- addAbsoluteParentXpathSearchCondition(cpsPathQuery, sqlStringBuilder, queryParameters, ACROSS_ALL_ANCHORS);
- addXpathSearchCondition(cpsPathQuery, sqlStringBuilder, queryParameters);
- addLeafConditions(cpsPathQuery, sqlStringBuilder);
- addTextFunctionCondition(cpsPathQuery, sqlStringBuilder, queryParameters);
- addContainsFunctionCondition(cpsPathQuery, sqlStringBuilder, queryParameters);
- if (PaginationOption.NO_PAGINATION != paginationOption) {
- sqlStringBuilder.append(" ORDER BY fragment.anchor_id");
- addPaginationCondition(sqlStringBuilder, queryParameters, paginationOption);
- }
- final Query query = entityManager.createNativeQuery(sqlStringBuilder.toString());
+ sqlStringBuilder.append("SELECT distinct(fragment.anchor_id) FROM fragment");
+ addWhereClauseForDataspace(dataspaceEntity, sqlStringBuilder, queryParameters);
+ addNodeSearchConditions(cpsPathQuery, sqlStringBuilder, queryParameters, true);
+ sqlStringBuilder.append(" ORDER BY fragment.anchor_id");
+ addPaginationCondition(sqlStringBuilder, queryParameters, paginationOption);
+
+ return getQuery(sqlStringBuilder.toString(), queryParameters, Long.class);
+ }
+
+ private Query getQuery(final String sql, final Map<String, Object> queryParameters, final Class<?> returnType) {
+ final Query query = entityManager.createNativeQuery(sql, returnType);
setQueryParameters(query, queryParameters);
return query;
}
- private Query getQueryForDataspaceOrAnchorAndCpsPath(final DataspaceEntity dataspaceEntity,
- final AnchorEntity anchorEntity,
- final CpsPathQuery cpsPathQuery,
- final List<Long> anchorIdsForPagination) {
- final StringBuilder sqlStringBuilder = new StringBuilder();
- final Map<String, Object> queryParameters = new HashMap<>();
+ private static void addWhereClauseForAnchor(final AnchorEntity anchorEntity,
+ final StringBuilder sqlStringBuilder,
+ final Map<String, Object> queryParameters) {
+ sqlStringBuilder.append(" WHERE anchor_id = :anchorId");
+ queryParameters.put("anchorId", anchorEntity.getId());
+ }
- if (anchorEntity == ACROSS_ALL_ANCHORS) {
- sqlStringBuilder.append("SELECT fragment.* FROM fragment JOIN anchor ON anchor.id = fragment.anchor_id"
- + " WHERE dataspace_id = :dataspaceId");
- queryParameters.put("dataspaceId", dataspaceEntity.getId());
- if (!anchorIdsForPagination.isEmpty()) {
- sqlStringBuilder.append(" AND anchor_id IN (:anchorIdsForPagination)");
- queryParameters.put("anchorIdsForPagination", anchorIdsForPagination);
- }
- } else {
- sqlStringBuilder.append("SELECT * FROM fragment WHERE anchor_id = :anchorId");
- queryParameters.put("anchorId", anchorEntity.getId());
- }
- addAbsoluteParentXpathSearchCondition(cpsPathQuery, sqlStringBuilder, queryParameters, anchorEntity);
+ private static void addWhereClauseForAnchorIds(final List<Long> anchorIdsForPagination,
+ final StringBuilder sqlStringBuilder,
+ final Map<String, Object> queryParameters) {
+ sqlStringBuilder.append(" WHERE anchor_id IN (:anchorIdsForPagination)");
+ queryParameters.put("anchorIdsForPagination", anchorIdsForPagination);
+ }
+
+ private static void addWhereClauseForDataspace(final DataspaceEntity dataspaceEntity,
+ final StringBuilder sqlStringBuilder,
+ final Map<String, Object> queryParameters) {
+ sqlStringBuilder.append(" JOIN anchor ON anchor.id = fragment.anchor_id WHERE dataspace_id = :dataspaceId");
+ queryParameters.put("dataspaceId", dataspaceEntity.getId());
+ }
+
+ private static void addNodeSearchConditions(final CpsPathQuery cpsPathQuery,
+ final StringBuilder sqlStringBuilder,
+ final Map<String, Object> queryParameters,
+ final boolean acrossAnchors) {
+ addAbsoluteParentXpathSearchCondition(cpsPathQuery, sqlStringBuilder, queryParameters, acrossAnchors);
addXpathSearchCondition(cpsPathQuery, sqlStringBuilder, queryParameters);
addLeafConditions(cpsPathQuery, sqlStringBuilder);
addTextFunctionCondition(cpsPathQuery, sqlStringBuilder, queryParameters);
addContainsFunctionCondition(cpsPathQuery, sqlStringBuilder, queryParameters);
-
- final Query query = entityManager.createNativeQuery(sqlStringBuilder.toString(), FragmentEntity.class);
- setQueryParameters(query, queryParameters);
- return query;
}
private static void addXpathSearchCondition(final CpsPathQuery cpsPathQuery,
@@ -152,12 +164,12 @@ public class FragmentQueryBuilder {
private static void addAbsoluteParentXpathSearchCondition(final CpsPathQuery cpsPathQuery,
final StringBuilder sqlStringBuilder,
final Map<String, Object> queryParameters,
- final AnchorEntity anchorEntity) {
+ final boolean acrossAnchors) {
if (CpsPathPrefixType.ABSOLUTE.equals(cpsPathQuery.getCpsPathPrefixType())) {
if (cpsPathQuery.getNormalizedParentPath().isEmpty()) {
sqlStringBuilder.append(" AND parent_id IS NULL");
} else {
- if (anchorEntity == ACROSS_ALL_ANCHORS) {
+ if (acrossAnchors) {
sqlStringBuilder.append(" AND parent_id IN (SELECT id FROM fragment WHERE xpath = :parentXpath)");
} else {
sqlStringBuilder.append(" AND parent_id = (SELECT id FROM fragment WHERE xpath = :parentXpath"
@@ -171,10 +183,12 @@ public class FragmentQueryBuilder {
private static void addPaginationCondition(final StringBuilder sqlStringBuilder,
final Map<String, Object> queryParameters,
final PaginationOption paginationOption) {
- final Integer offset = (paginationOption.getPageIndex() - 1) * paginationOption.getPageSize();
- sqlStringBuilder.append(" LIMIT :pageSize OFFSET :offset");
- queryParameters.put("pageSize", paginationOption.getPageSize());
- queryParameters.put("offset", offset);
+ if (PaginationOption.NO_PAGINATION != paginationOption) {
+ final Integer offset = (paginationOption.getPageIndex() - 1) * paginationOption.getPageSize();
+ sqlStringBuilder.append(" LIMIT :pageSize OFFSET :offset");
+ queryParameters.put("pageSize", paginationOption.getPageSize());
+ queryParameters.put("offset", offset);
+ }
}
private static Integer getTextValueAsInt(final CpsPathQuery cpsPathQuery) {
@@ -185,40 +199,34 @@ public class FragmentQueryBuilder {
}
}
- private void addLeafConditions(final CpsPathQuery cpsPathQuery, final StringBuilder sqlStringBuilder) {
+ private static void addLeafConditions(final CpsPathQuery cpsPathQuery, final StringBuilder sqlStringBuilder) {
if (cpsPathQuery.hasLeafConditions()) {
- queryLeafConditions(cpsPathQuery, sqlStringBuilder);
- }
- }
-
- private void queryLeafConditions(final CpsPathQuery cpsPathQuery, final StringBuilder sqlStringBuilder) {
- sqlStringBuilder.append(" AND (");
- final Queue<String> booleanOperatorsQueue = new LinkedList<>(cpsPathQuery.getBooleanOperators());
- final Queue<String> comparativeOperatorQueue = new LinkedList<>(cpsPathQuery.getComparativeOperators());
- cpsPathQuery.getLeavesData().forEach(leaf -> {
- final String nextComparativeOperator = comparativeOperatorQueue.poll();
- if (leaf.getValue() instanceof Integer) {
- sqlStringBuilder.append("(attributes ->> '").append(leaf.getName()).append("')\\:\\:int");
- sqlStringBuilder.append(nextComparativeOperator);
- sqlStringBuilder.append(leaf.getValue());
- } else {
- if ("=".equals(nextComparativeOperator)) {
- final String leafValueAsText = leaf.getValue().toString();
- sqlStringBuilder.append("attributes ->> '").append(leaf.getName()).append("'");
- sqlStringBuilder.append(" = '");
- sqlStringBuilder.append(EscapeUtils.escapeForSqlStringLiteral(leafValueAsText));
- sqlStringBuilder.append("'");
+ sqlStringBuilder.append(" AND (");
+ final Queue<String> booleanOperatorsQueue = new LinkedList<>(cpsPathQuery.getBooleanOperators());
+ cpsPathQuery.getLeafConditions().forEach(leafCondition -> {
+ if (leafCondition.value() instanceof Integer) {
+ sqlStringBuilder.append("(attributes ->> '").append(leafCondition.name()).append("')\\:\\:int");
+ sqlStringBuilder.append(leafCondition.operator());
+ sqlStringBuilder.append(leafCondition.value());
} else {
- throw new CpsPathException(" can use only " + nextComparativeOperator + " with integer ");
+ if ("=".equals(leafCondition.operator())) {
+ final String leafValueAsText = leafCondition.value().toString();
+ sqlStringBuilder.append("attributes ->> '").append(leafCondition.name()).append("'");
+ sqlStringBuilder.append(" = '");
+ sqlStringBuilder.append(EscapeUtils.escapeForSqlStringLiteral(leafValueAsText));
+ sqlStringBuilder.append("'");
+ } else {
+ throw new CpsPathException(" can use only " + leafCondition.operator() + " with integer ");
+ }
}
- }
- if (!booleanOperatorsQueue.isEmpty()) {
- sqlStringBuilder.append(" ");
- sqlStringBuilder.append(booleanOperatorsQueue.poll());
- sqlStringBuilder.append(" ");
- }
- });
- sqlStringBuilder.append(")");
+ if (!booleanOperatorsQueue.isEmpty()) {
+ sqlStringBuilder.append(" ");
+ sqlStringBuilder.append(booleanOperatorsQueue.poll());
+ sqlStringBuilder.append(" ");
+ }
+ });
+ sqlStringBuilder.append(")");
+ }
}
private static void addTextFunctionCondition(final CpsPathQuery cpsPathQuery,
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java
index 1a31d2b499..9598230cdb 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java
@@ -1,140 +1,134 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation.
- * Modifications Copyright (C) 2020-2021 Bell Canada.
- * Modifications Copyright (C) 2020-2021 Pantheon.tech.
- * Modifications Copyright (C) 2023 TechMahindra Ltd.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.spi.repository;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.FragmentEntity;
-import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
-import org.onap.cps.spi.utils.EscapeUtils;
-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;
-
-@Repository
-public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, FragmentRepositoryCpsPathQuery,
- FragmentPrefetchRepository {
-
- Optional<FragmentEntity> findByAnchorAndXpath(AnchorEntity anchorEntity, String xpath);
-
- default FragmentEntity getByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) {
- return findByAnchorAndXpath(anchorEntity, xpath).orElseThrow(() ->
- new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath));
- }
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)",
- nativeQuery = true)
- List<FragmentEntity> findByAnchorIdAndXpathIn(@Param("anchorId") long anchorId,
- @Param("xpaths") String[] xpaths);
-
- default List<FragmentEntity> findByAnchorAndXpathIn(final AnchorEntity anchorEntity,
- final Collection<String> xpaths) {
- return findByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths.toArray(new String[0]));
- }
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId \n"
- + "AND xpath LIKE :escapedXpath||'[@%]' AND xpath NOT LIKE :escapedXpath||'[@%]/%[@%]'",
- nativeQuery = true)
- List<FragmentEntity> findListByAnchorIdAndEscapedXpath(@Param("anchorId") long anchorId,
- @Param("escapedXpath") String escapedXpath);
-
- default List<FragmentEntity> findListByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) {
- final String escapedXpath = EscapeUtils.escapeForSqlLike(xpath);
- return findListByAnchorIdAndEscapedXpath(anchorEntity.getId(), escapedXpath);
- }
-
- @Query(value = "SELECT fragment.* FROM fragment JOIN anchor ON anchor.id = fragment.anchor_id "
- + "WHERE dataspace_id = :dataspaceId AND xpath = ANY (:xpaths)", nativeQuery = true)
- List<FragmentEntity> findByDataspaceIdAndXpathIn(@Param("dataspaceId") int dataspaceId,
- @Param("xpaths") String[] xpaths);
-
- default List<FragmentEntity> findByDataspaceAndXpathIn(final DataspaceEntity dataspaceEntity,
- final Collection<String> xpaths) {
- return findByDataspaceIdAndXpathIn(dataspaceEntity.getId(), xpaths.toArray(new String[0]));
- }
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id IN (:anchorIds)"
- + " AND xpath = ANY (:xpaths)", nativeQuery = true)
- List<FragmentEntity> findByAnchorIdsAndXpathIn(@Param("anchorIds") Long[] anchorIds,
- @Param("xpaths") String[] xpaths);
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId LIMIT 1", nativeQuery = true)
- Optional<FragmentEntity> findOneByAnchorId(@Param("anchorId") long anchorId);
-
- @Modifying
- @Query(value = "DELETE FROM fragment WHERE anchor_id = ANY (:anchorIds)", nativeQuery = true)
- void deleteByAnchorIdIn(@Param("anchorIds") long[] anchorIds);
-
- default void deleteByAnchorIn(final Collection<AnchorEntity> anchorEntities) {
- deleteByAnchorIdIn(anchorEntities.stream().map(AnchorEntity::getId).mapToLong(id -> id).toArray());
- }
-
- @Modifying
- @Query(value = "DELETE FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)", nativeQuery = true)
- void deleteByAnchorIdAndXpaths(@Param("anchorId") long anchorId, @Param("xpaths") String[] xpaths);
-
- default void deleteByAnchorIdAndXpaths(final long 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") long anchorId,
- @Param("xpathPatterns") String[] xpathPatterns);
-
- default void deleteListsByAnchorIdAndXpaths(long anchorId, Collection<String> xpaths) {
- deleteByAnchorIdAndXpathLikeAny(anchorId,
- xpaths.stream().map(xpath -> EscapeUtils.escapeForSqlLike(xpath) + "[@%").toArray(String[]::new));
- }
-
- @Query(value = "SELECT xpath FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)",
- nativeQuery = true)
- List<String> findAllXpathByAnchorIdAndXpathIn(@Param("anchorId") long 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]));
- }
-
- @Query(value = "SELECT EXISTS(SELECT 1 FROM fragment WHERE anchor_id = :anchorId"
- + " AND xpath LIKE :xpathPattern LIMIT 1)", nativeQuery = true)
- boolean existsByAnchorIdAndParentXpathAndXpathLike(@Param("anchorId") long anchorId,
- @Param("xpathPattern") String xpathPattern);
-
- default boolean existsByAnchorAndXpathStartsWith(final AnchorEntity anchorEntity, final String xpath) {
- return existsByAnchorIdAndParentXpathAndXpathLike(anchorEntity.getId(),
- EscapeUtils.escapeForSqlLike(xpath) + "%");
- }
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND parent_id IS NULL", nativeQuery = true)
- List<FragmentEntity> findRootsByAnchorId(@Param("anchorId") long anchorId);
-
-}
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2024 Nordix Foundation.
+ * Modifications Copyright (C) 2020-2021 Bell Canada.
+ * Modifications Copyright (C) 2020-2021 Pantheon.tech.
+ * Modifications Copyright (C) 2023 TechMahindra Ltd.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ri.repository;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.FragmentEntity;
+import org.onap.cps.ri.utils.EscapeUtils;
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
+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;
+
+@Repository
+public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, FragmentRepositoryCpsPathQuery,
+ FragmentPrefetchRepository {
+
+ Optional<FragmentEntity> findByAnchorAndXpath(AnchorEntity anchorEntity, String xpath);
+
+ default FragmentEntity getByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) {
+ return findByAnchorAndXpath(anchorEntity, xpath).orElseThrow(() ->
+ new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath));
+ }
+
+ @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND xpath IN (:xpaths)",
+ nativeQuery = true)
+ List<FragmentEntity> findByAnchorIdAndXpathIn(@Param("anchorId") long anchorId,
+ @Param("xpaths") Collection<String> xpaths);
+
+ default List<FragmentEntity> findByAnchorAndXpathIn(final AnchorEntity anchorEntity,
+ final Collection<String> xpaths) {
+ return findByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths);
+ }
+
+ @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId \n"
+ + "AND xpath LIKE :escapedXpath||'[@%]' AND xpath NOT LIKE :escapedXpath||'[@%]/%[@%]'",
+ nativeQuery = true)
+ List<FragmentEntity> findListByAnchorIdAndEscapedXpath(@Param("anchorId") long anchorId,
+ @Param("escapedXpath") String escapedXpath);
+
+ default List<FragmentEntity> findListByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) {
+ final String escapedXpath = EscapeUtils.escapeForSqlLike(xpath);
+ return findListByAnchorIdAndEscapedXpath(anchorEntity.getId(), escapedXpath);
+ }
+
+ @Query(value = "SELECT fragment.* FROM fragment JOIN anchor ON anchor.id = fragment.anchor_id "
+ + "WHERE dataspace_id = :dataspaceId AND xpath IN (:xpaths)", nativeQuery = true)
+ List<FragmentEntity> findByDataspaceIdAndXpathIn(@Param("dataspaceId") int dataspaceId,
+ @Param("xpaths") Collection<String> xpaths);
+
+ default List<FragmentEntity> findByDataspaceAndXpathIn(final DataspaceEntity dataspaceEntity,
+ final Collection<String> xpaths) {
+ return findByDataspaceIdAndXpathIn(dataspaceEntity.getId(), xpaths);
+ }
+
+ @Query(value = "SELECT * FROM fragment WHERE anchor_id IN (:anchorIds)"
+ + " AND xpath IN (:xpaths)", nativeQuery = true)
+ List<FragmentEntity> findByAnchorIdsAndXpathIn(@Param("anchorIds") Collection<Long> anchorIds,
+ @Param("xpaths") Collection<String> xpaths);
+
+ @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId LIMIT 1", nativeQuery = true)
+ Optional<FragmentEntity> findOneByAnchorId(@Param("anchorId") long anchorId);
+
+ @Modifying
+ @Query(value = "DELETE FROM fragment WHERE anchor_id IN (:anchorIds)", nativeQuery = true)
+ void deleteByAnchorIdIn(@Param("anchorIds") Collection<Long> anchorIds);
+
+ default void deleteByAnchorIn(final Collection<AnchorEntity> anchorEntities) {
+ deleteByAnchorIdIn(anchorEntities.stream().map(AnchorEntity::getId).toList());
+ }
+
+ @Modifying
+ @Query(value = "DELETE FROM fragment WHERE anchor_id = :anchorId AND xpath IN (:xpaths)", nativeQuery = true)
+ void deleteByAnchorIdAndXpaths(@Param("anchorId") long anchorId, @Param("xpaths") Collection<String> xpaths);
+
+ @Modifying
+ @Query(value = "DELETE FROM fragment f WHERE anchor_id = :anchorId AND xpath LIKE :xpathPattern",
+ nativeQuery = true)
+ void deleteByAnchorIdAndXpathLike(@Param("anchorId") long anchorId, @Param("xpathPattern") String xpathPattern);
+
+ default void deleteListByAnchorIdAndXpath(final long anchorId, final String xpath) {
+ deleteByAnchorIdAndXpathLike(anchorId, EscapeUtils.escapeForSqlLike(xpath) + "[@%");
+ }
+
+ @Query(value = "SELECT xpath FROM fragment WHERE anchor_id = :anchorId AND xpath IN (:xpaths)",
+ nativeQuery = true)
+ List<String> findAllXpathByAnchorIdAndXpathIn(@Param("anchorId") long anchorId,
+ @Param("xpaths") Collection<String> xpaths);
+
+ default List<String> findAllXpathByAnchorAndXpathIn(final AnchorEntity anchorEntity,
+ final Collection<String> xpaths) {
+ return findAllXpathByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths);
+ }
+
+ @Query(value = "SELECT EXISTS(SELECT 1 FROM fragment WHERE anchor_id = :anchorId"
+ + " AND xpath LIKE :xpathPattern LIMIT 1)", nativeQuery = true)
+ boolean existsByAnchorIdAndParentXpathAndXpathLike(@Param("anchorId") long anchorId,
+ @Param("xpathPattern") String xpathPattern);
+
+ default boolean existsByAnchorAndXpathStartsWith(final AnchorEntity anchorEntity, final String xpath) {
+ return existsByAnchorIdAndParentXpathAndXpathLike(anchorEntity.getId(),
+ EscapeUtils.escapeForSqlLike(xpath) + "%");
+ }
+
+ @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND parent_id IS NULL", nativeQuery = true)
+ List<FragmentEntity> findRootsByAnchorId(@Param("anchorId") long anchorId);
+
+}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java
index 9c279618b0..49c8e76ab5 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java
@@ -19,14 +19,14 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.util.List;
import org.onap.cps.cpspath.parser.CpsPathQuery;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.FragmentEntity;
import org.onap.cps.spi.PaginationOption;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.FragmentEntity;
public interface FragmentRepositoryCpsPathQuery {
List<FragmentEntity> findByAnchorAndCpsPath(AnchorEntity anchorEntity, CpsPathQuery cpsPathQuery);
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java
index 78e0f08c44..01b2813cd3 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java
@@ -1,6 +1,6 @@
/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation.
+ * Copyright (C) 2021-2024 Nordix Foundation.
* Modifications Copyright (C) 2023 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,28 +19,23 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;
import jakarta.transaction.Transactional;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.cpspath.parser.CpsPathQuery;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.FragmentEntity;
import org.onap.cps.spi.PaginationOption;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.FragmentEntity;
@RequiredArgsConstructor
@Slf4j
public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCpsPathQuery {
- @PersistenceContext
- private EntityManager entityManager;
-
private final FragmentQueryBuilder fragmentQueryBuilder;
@Override
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceQuery.java
index 00e53aa00f..ad0f9c5c61 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceQuery.java
@@ -1,6 +1,6 @@
/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation.
+ * Copyright (C) 2022-2024 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,9 +18,10 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.util.Collection;
+import java.util.Map;
import org.onap.cps.spi.model.ModuleReference;
/**
@@ -29,4 +30,8 @@ import org.onap.cps.spi.model.ModuleReference;
public interface ModuleReferenceQuery {
Collection<ModuleReference> identifyNewModuleReferences(final Collection<ModuleReference> moduleReferencesToCheck);
+
+ Collection<ModuleReference> findModuleReferences(final String dataspaceName, final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes);
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepository.java
index 15ffa372f9..e9b866cc19 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepository.java
@@ -18,9 +18,9 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
-import org.onap.cps.spi.entities.YangResourceEntity;
+import org.onap.cps.ri.models.YangResourceEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java
new file mode 100644
index 0000000000..4ca02a9c3c
--- /dev/null
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java
@@ -0,0 +1,181 @@
+/*-
+ * ============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.ri.repository;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
+import jakarta.persistence.Query;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.spi.model.ModuleReference;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@Transactional
+@RequiredArgsConstructor
+public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ private final TempTableCreator tempTableCreator;
+
+ @Override
+ @SneakyThrows
+ public Collection<ModuleReference> identifyNewModuleReferences(
+ final Collection<ModuleReference> moduleReferencesToCheck) {
+
+ if (moduleReferencesToCheck == null || moduleReferencesToCheck.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ final Collection<List<String>> sqlData = new HashSet<>(moduleReferencesToCheck.size());
+ for (final ModuleReference moduleReference : moduleReferencesToCheck) {
+ final List<String> row = new ArrayList<>(2);
+ row.add(moduleReference.getModuleName());
+ row.add(moduleReference.getRevision());
+ sqlData.add(row);
+ }
+
+ final String tempTableName = tempTableCreator.createTemporaryTable(
+ "moduleReferencesToCheckTemp", sqlData, List.of("module_name", "revision"));
+
+ return identifyNewModuleReferencesForCmHandle(tempTableName);
+ }
+
+ /**
+ * Finds module references based on specified dataspace, anchor, and attribute filters.
+ * This method constructs and executes a SQL query to retrieve module references. The query applies filters to
+ * parent and child fragments using the provided attribute maps. The `parentAttributes` are used to filter
+ * parent fragments, while `childAttributes` filter child fragments.
+ *
+ * @param dataspaceName the name of the dataspace to filter on.
+ * @param anchorName the name of the anchor to filter on.
+ * @param parentAttributes a map of attributes for filtering parent fragments.
+ * @param childAttributes a map of attributes for filtering child fragments.
+ * @return a collection of {@link ModuleReference} objects that match the specified filters.
+ */
+ @Transactional
+ @SuppressWarnings("unchecked")
+ @Override
+ public Collection<ModuleReference> findModuleReferences(final String dataspaceName, final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes) {
+
+ final String parentFragmentWhereClause = buildWhereClause(childAttributes, "parentFragment");
+ final String childFragmentWhereClause = buildWhereClause(parentAttributes, "childFragment");
+
+ final String moduleReferencesSqlQuery = buildModuleReferencesSqlQuery(parentFragmentWhereClause,
+ childFragmentWhereClause);
+
+ final Query query = entityManager.createNativeQuery(moduleReferencesSqlQuery);
+ setQueryParameters(query, parentAttributes, childAttributes, anchorName, dataspaceName);
+ return processQueryResults(query.getResultList());
+ }
+
+ private String buildWhereClause(final Map<String, String> attributes, final String alias) {
+ return attributes.keySet().stream()
+ .map(attributeName -> String.format("%s.attributes->>'%s' = ?", alias, attributeName))
+ .collect(Collectors.joining(" AND "));
+ }
+
+ private void setQueryParameters(final Query query, final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes, final String anchorName,
+ final String dataspaceName) {
+ final String childAttributeValue = childAttributes.entrySet().iterator().next().getValue();
+ query.setParameter(1, childAttributeValue);
+
+ final String parentAttributeValue = parentAttributes.entrySet().iterator().next().getValue();
+ query.setParameter(2, parentAttributeValue);
+
+ query.setParameter(3, anchorName);
+ query.setParameter(4, dataspaceName);
+ }
+
+ @SuppressFBWarnings(value = "VA_FORMAT_STRING_USES_NEWLINE", justification = "no \n in string just in file format")
+ private String buildModuleReferencesSqlQuery(final String parentFragmentClause, final String childFragmentClause) {
+ return """
+ WITH Fragment AS (
+ SELECT childFragment.attributes->>'id' AS schema_set_name
+ FROM fragment parentFragment
+ JOIN fragment childFragment ON parentFragment.parent_id = childFragment.id
+ JOIN anchor anchorInfo ON parentFragment.anchor_id = anchorInfo.id
+ JOIN dataspace dataspaceInfo ON anchorInfo.dataspace_id = dataspaceInfo.id
+ WHERE %s
+ AND %s
+ AND anchorInfo.name = ?
+ AND dataspaceInfo.name = ?
+ LIMIT 1
+ ),
+ SchemaSet AS (
+ SELECT id
+ FROM schema_set
+ WHERE name = (SELECT schema_set_name FROM Fragment)
+ )
+ SELECT yangResource.module_name, yangResource.revision
+ FROM yang_resource yangResource
+ JOIN schema_set_yang_resources schemaSetYangResources
+ ON yangResource.id = schemaSetYangResources.yang_resource_id
+ WHERE schemaSetYangResources.schema_set_id = (SELECT id FROM SchemaSet);
+ """.formatted(parentFragmentClause, childFragmentClause);
+ }
+
+ private Collection<ModuleReference> processQueryResults(final List<Object[]> queryResults) {
+ if (queryResults.isEmpty()) {
+ log.info("No module references found for the provided attributes.");
+ return Collections.emptyList();
+ }
+ return queryResults.stream()
+ .map(queryResult -> {
+ final String name = (String) queryResult[0];
+ final String revision = (String) queryResult[1];
+ return new ModuleReference(name, revision);
+ })
+ .collect(Collectors.toList());
+ }
+
+ private Collection<ModuleReference> identifyNewModuleReferencesForCmHandle(final String tempTableName) {
+ final String sql = String.format(
+ "SELECT %1$s.module_name, %1$s.revision"
+ + " FROM %1$s LEFT JOIN yang_resource"
+ + " ON yang_resource.module_name=%1$s.module_name"
+ + " AND yang_resource.revision=%1$s.revision"
+ + " WHERE yang_resource.module_name IS NULL;", tempTableName);
+
+ @SuppressWarnings("unchecked")
+ final List<Object[]> resultsAsObjects = entityManager.createNativeQuery(sql).getResultList();
+
+ final List<ModuleReference> resultsAsModuleReferences = new ArrayList<>(resultsAsObjects.size());
+ for (final Object[] row : resultsAsObjects) {
+ resultsAsModuleReferences.add(new ModuleReference((String) row[0], (String) row[1]));
+ }
+ return resultsAsModuleReferences;
+ }
+}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java
index 3c5f973cb0..b455bc04c0 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2020 Pantheon.tech
* Modifications Copyright (C) 2022 TechMahindra Ltd.
- * Modifications Copyright (C) 2023 Nordix Foundation
+ * Modifications Copyright (C) 2023-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,13 +19,13 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.SchemaSetEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.SchemaSetEntity;
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
@@ -61,10 +61,10 @@ public interface SchemaSetRepository extends JpaRepository<SchemaSetEntity, Inte
}
@Modifying
- @Query(value = "DELETE FROM schema_set WHERE dataspace_id = :dataspaceId AND name = ANY (:schemaSetNames)",
+ @Query(value = "DELETE FROM schema_set WHERE dataspace_id = :dataspaceId AND name IN (:schemaSetNames)",
nativeQuery = true)
void deleteByDataspaceIdAndNameIn(@Param("dataspaceId") final int dataspaceId,
- @Param("schemaSetNames") final String[] schemaSetNames);
+ @Param("schemaSetNames") final Collection<String> schemaSetNames);
/**
* Delete multiple schema sets in a given dataspace.
@@ -73,7 +73,7 @@ public interface SchemaSetRepository extends JpaRepository<SchemaSetEntity, Inte
*/
default void deleteByDataspaceAndNameIn(final DataspaceEntity dataspaceEntity,
final Collection<String> schemaSetNames) {
- deleteByDataspaceIdAndNameIn(dataspaceEntity.getId(), schemaSetNames.toArray(new String[0]));
+ deleteByDataspaceIdAndNameIn(dataspaceEntity.getId(), schemaSetNames);
}
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepository.java
index aacebd63b3..8350d5728c 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepository.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.util.List;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepositoryImpl.java
index c786a62d0c..287bcda01b 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepositoryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepositoryImpl.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/TempTableCreator.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java
index 5804b2dc5d..25c1491502 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/TempTableCreator.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java
@@ -1,6 +1,6 @@
/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation.
+ * Copyright (C) 2022-2024 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,20 +18,18 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
-import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.spi.utils.EscapeUtils;
+import org.onap.cps.ri.utils.EscapeUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -54,7 +52,7 @@ public class TempTableCreator {
*/
public String createTemporaryTable(final String prefix,
final Collection<List<String>> sqlData,
- final String... columnNames) {
+ final Collection<String> columnNames) {
final String tempTableName = prefix + UUID.randomUUID().toString().replace("-", "");
final StringBuilder sqlStringBuilder = new StringBuilder("CREATE TEMPORARY TABLE ");
sqlStringBuilder.append(tempTableName);
@@ -65,29 +63,21 @@ public class TempTableCreator {
return tempTableName;
}
- private static void defineColumns(final StringBuilder sqlStringBuilder, final String[] columnNames) {
- sqlStringBuilder.append('(');
- final Iterator<String> it = Arrays.stream(columnNames).iterator();
- while (it.hasNext()) {
- final String columnName = it.next();
- sqlStringBuilder.append(" ");
- sqlStringBuilder.append(columnName);
- sqlStringBuilder.append(" varchar NOT NULL");
- if (it.hasNext()) {
- sqlStringBuilder.append(",");
- }
- }
- sqlStringBuilder.append(")");
+ private static void defineColumns(final StringBuilder sqlStringBuilder, final Collection<String> columnNames) {
+ final String columns = columnNames.stream()
+ .map(columnName -> " " + columnName + " varchar NOT NULL")
+ .collect(Collectors.joining(","));
+ sqlStringBuilder.append('(').append(columns).append(')');
}
private static void insertData(final StringBuilder sqlStringBuilder,
final String tempTableName,
- final String[] columnNames,
+ final Collection<String> columnNames,
final Collection<List<String>> sqlData) {
final Collection<String> sqlInserts = new HashSet<>(sqlData.size());
for (final Collection<String> rowValues : sqlData) {
final Collection<String> escapedValues =
- rowValues.stream().map(EscapeUtils::escapeForSqlStringLiteral).collect(Collectors.toList());
+ rowValues.stream().map(EscapeUtils::escapeForSqlStringLiteral).toList();
sqlInserts.add("('" + String.join("','", escapedValues) + "')");
}
sqlStringBuilder.append("INSERT INTO ");
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepository.java
index 9ae32b3e78..ef7b12dc9c 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepository.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.util.Collection;
import java.util.List;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepositoryImpl.java
index c84ff427e6..c65ab7d6fa 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepositoryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepositoryImpl.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java
index 8be0d9a33b..831766cc9a 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java
@@ -18,13 +18,13 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.repository;
+package org.onap.cps.ri.repository;
import java.util.Collection;
import java.util.List;
import java.util.Set;
-import org.onap.cps.spi.entities.YangResourceEntity;
-import org.onap.cps.spi.entities.YangResourceModuleReference;
+import org.onap.cps.ri.models.YangResourceEntity;
+import org.onap.cps.ri.models.YangResourceModuleReference;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
@@ -35,11 +35,7 @@ import org.springframework.stereotype.Repository;
public interface YangResourceRepository extends JpaRepository<YangResourceEntity, Integer>,
YangResourceNativeRepository, SchemaSetYangResourceRepository {
- List<YangResourceEntity> findAllByChecksumIn(String[] checksums);
-
- default List<YangResourceEntity> findAllByChecksumIn(final Collection<String> checksums) {
- return findAllByChecksumIn(checksums.toArray(new String[0]));
- }
+ List<YangResourceEntity> findAllByChecksumIn(Collection<String> checksums);
@Query(value = """
SELECT DISTINCT
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/config/CpsSessionFactory.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/CpsSessionFactory.java
index 5241ea0096..c0291176f4 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/config/CpsSessionFactory.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/CpsSessionFactory.java
@@ -18,15 +18,15 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.config;
+package org.onap.cps.ri.utils;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.SchemaSetEntity;
-import org.onap.cps.spi.entities.YangResourceEntity;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.models.SchemaSetEntity;
+import org.onap.cps.ri.models.YangResourceEntity;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/utils/CpsValidatorImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/CpsValidatorImpl.java
index c727388b25..4f942a37ea 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/utils/CpsValidatorImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/CpsValidatorImpl.java
@@ -18,16 +18,16 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.impl.utils;
+package org.onap.cps.ri.utils;
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.spi.PaginationOption;
import org.onap.cps.spi.exceptions.DataValidationException;
-import org.onap.cps.spi.utils.CpsValidator;
import org.springframework.stereotype.Component;
@Slf4j
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/utils/EscapeUtils.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/EscapeUtils.java
index 2b61d39503..5323ae6bc9 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/utils/EscapeUtils.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/EscapeUtils.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.utils;
+package org.onap.cps.ri.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/SessionManager.java
index 6150bf9dbe..b81a0bd39d 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/SessionManager.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.utils;
+package org.onap.cps.ri.utils;
import com.google.common.util.concurrent.TimeLimiter;
import com.google.common.util.concurrent.UncheckedExecutionException;
@@ -36,13 +36,12 @@ import lombok.extern.slf4j.Slf4j;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Session;
-import org.onap.cps.spi.config.CpsSessionFactory;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
+import org.onap.cps.ri.models.AnchorEntity;
+import org.onap.cps.ri.models.DataspaceEntity;
+import org.onap.cps.ri.repository.AnchorRepository;
+import org.onap.cps.ri.repository.DataspaceRepository;
import org.onap.cps.spi.exceptions.SessionManagerException;
import org.onap.cps.spi.exceptions.SessionTimeoutException;
-import org.onap.cps.spi.repository.AnchorRepository;
-import org.onap.cps.spi.repository.DataspaceRepository;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/utils/TimeLimiterProvider.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/TimeLimiterProvider.java
index 2bd7ac3763..10031c0b28 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/utils/TimeLimiterProvider.java
+++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/TimeLimiterProvider.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.utils;
+package org.onap.cps.ri.utils;
import com.google.common.util.concurrent.SimpleTimeLimiter;
import com.google.common.util.concurrent.TimeLimiter;
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java
deleted file mode 100644
index 454848b98f..0000000000
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java
+++ /dev/null
@@ -1,87 +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.spi.repository;
-
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.PersistenceContext;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import lombok.AllArgsConstructor;
-import lombok.SneakyThrows;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.spi.model.ModuleReference;
-import org.springframework.transaction.annotation.Transactional;
-
-@Slf4j
-@Transactional
-@AllArgsConstructor
-public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery {
-
- @PersistenceContext
- private EntityManager entityManager;
-
- private TempTableCreator tempTableCreator;
-
- @Override
- @SneakyThrows
- public Collection<ModuleReference> identifyNewModuleReferences(
- final Collection<ModuleReference> moduleReferencesToCheck) {
-
- if (moduleReferencesToCheck == null || moduleReferencesToCheck.isEmpty()) {
- return Collections.emptyList();
- }
-
- final Collection<List<String>> sqlData = new HashSet<>(moduleReferencesToCheck.size());
- for (final ModuleReference moduleReference : moduleReferencesToCheck) {
- final List<String> row = new ArrayList<>(2);
- row.add(moduleReference.getModuleName());
- row.add(moduleReference.getRevision());
- sqlData.add(row);
- }
-
- final String tempTableName = tempTableCreator.createTemporaryTable(
- "moduleReferencesToCheckTemp", sqlData, "module_name", "revision");
-
- return identifyNewModuleReferencesForCmHandle(tempTableName);
- }
-
- private Collection<ModuleReference> identifyNewModuleReferencesForCmHandle(final String tempTableName) {
- final String sql = String.format(
- "SELECT %1$s.module_name, %1$s.revision"
- + " FROM %1$s LEFT JOIN yang_resource"
- + " ON yang_resource.module_name=%1$s.module_name"
- + " AND yang_resource.revision=%1$s.revision"
- + " WHERE yang_resource.module_name IS NULL;", tempTableName);
-
- @SuppressWarnings("unchecked")
- final List<Object[]> resultsAsObjects = entityManager.createNativeQuery(sql).getResultList();
-
- final List<ModuleReference> resultsAsModuleReferences = new ArrayList<>(resultsAsObjects.size());
- for (final Object[] row : resultsAsObjects) {
- resultsAsModuleReferences.add(new ModuleReference((String) row[0], (String) row[1]));
- }
-
- return resultsAsModuleReferences;
- }
-}
diff --git a/cps-ri/src/main/resources/changelog/db/changes/data/dmi/generated-csv/README.md b/cps-ri/src/main/resources/changelog/db/changes/data/dmi/generated-csv/README.md
deleted file mode 100644
index 212acb9006..0000000000
--- a/cps-ri/src/main/resources/changelog/db/changes/data/dmi/generated-csv/README.md
+++ /dev/null
@@ -1,23 +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=========================================================
--->
-
-##Placeholder folder for generated CSV files as part of yang models.
-
-Do not remove this folder \ No newline at end of file
diff --git a/cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2021-12-13.yang b/cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2021-12-13.yang
deleted file mode 100644
index ed3559bf61..0000000000
--- a/cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2021-12-13.yang
+++ /dev/null
@@ -1,63 +0,0 @@
-module dmi-registry {
-
- yang-version 1.1;
-
- namespace "org:onap:cps:ncmp";
-
- prefix dmi-reg;
-
- contact "dylan.byrne@est.tech";
-
- revision "2021-05-20" {
- description
- "Initial Version";
- }
-
- revision "2021-10-20" {
- description
- "Added dmi-data-service-name & dmi-model-service-name to allow separate DMI instances for each responsibility";
- }
-
- revision "2021-12-13" {
- description
- "Added new list of public additonal properties for a Cm-Handle which are exposed to clients of the NCMP interface";
- }
-
- container dmi-registry {
- list cm-handles {
- key "id";
- leaf id {
- type string;
- }
- leaf dmi-service-name {
- type string;
- }
- leaf dmi-data-service-name {
- type string;
- }
- leaf dmi-model-service-name {
- type string;
- }
-
- list additional-properties {
- key "name";
- leaf name {
- type string;
- }
- leaf value {
- type string;
- }
- }
-
- list public-properties {
- key "name";
- leaf name {
- type string;
- }
- leaf value {
- type string;
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2022-02-10.yang b/cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2022-02-10.yang
deleted file mode 100644
index 3c6d990c5d..0000000000
--- a/cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2022-02-10.yang
+++ /dev/null
@@ -1,81 +0,0 @@
-module dmi-registry {
-
- yang-version 1.1;
-
- namespace "org:onap:cps:ncmp";
-
- prefix dmi-reg;
-
- contact "toine.siebelink@est.tech";
-
- revision "2022-02-10" {
- description
- "Added State, LockReason, LockReasonDetails to aid with cmHandle sync and timestamp to aid with retry/timeout scenarios";
- }
-
- revision "2021-12-13" {
- description
- "Added new list of public additional properties for a Cm-Handle which are exposed to clients of the NCMP interface";
- }
-
- revision "2021-10-20" {
- description
- "Added dmi-data-service-name & dmi-model-service-name to allow separate DMI instances for each responsibility";
- }
-
- revision "2021-05-20" {
- description
- "Initial Version";
- }
-
- container dmi-registry {
- list cm-handles {
- key "id";
- leaf id {
- type string;
- }
- leaf dmi-service-name {
- type string;
- }
- leaf dmi-data-service-name {
- type string;
- }
- leaf dmi-model-service-name {
- type string;
- }
-
- list additional-properties {
- key "name";
- leaf name {
- type string;
- }
- leaf value {
- type string;
- }
- }
-
- list public-properties {
- key "name";
- leaf name {
- type string;
- }
- leaf value {
- type string;
- }
- }
-
- leaf state {
- type string;
- }
- leaf lock-reason {
- type string;
- }
- leaf lock-reason-details {
- type string;
- }
- leaf last-update-time {
- type string;
- }
- }
- }
-}
diff --git a/cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2022-05-10.yang b/cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2022-05-10.yang
deleted file mode 100644
index 77517968c6..0000000000
--- a/cps-ri/src/main/resources/changelog/db/changes/data/yang-models/dmi-registry@2022-05-10.yang
+++ /dev/null
@@ -1,123 +0,0 @@
-module dmi-registry {
-
- yang-version 1.1;
-
- namespace "org:onap:cps:ncmp";
-
- prefix dmi-reg;
-
- contact "toine.siebelink@est.tech";
-
- revision "2022-05-10" {
- description
- "Added DataSyncEnabled, SyncState with State, LastSyncTime, DataStoreSyncState with Operational and Running syncstate";
- }
-
- revision "2022-02-10" {
- description
- "Added State, LockReason, LockReasonDetails to aid with cmHandle sync and timestamp to aid with retry/timeout scenarios";
- }
-
- revision "2021-12-13" {
- description
- "Added new list of public additional properties for a Cm-Handle which are exposed to clients of the NCMP interface";
- }
-
- revision "2021-10-20" {
- description
- "Added dmi-data-service-name & dmi-model-service-name to allow separate DMI instances for each responsibility";
- }
-
- revision "2021-05-20" {
- description
- "Initial Version";
- }
-
- grouping LockReason {
- leaf reason {
- type string;
- }
- leaf details {
- type string;
- }
- }
-
- grouping SyncState {
- leaf sync-state {
- type string;
- }
- leaf last-sync-time {
- type string;
- }
- }
-
- grouping Datastores {
- container operational {
- uses SyncState;
- }
- container running {
- uses SyncState;
- }
- }
-
- container dmi-registry {
- list cm-handles {
- key "id";
- leaf id {
- type string;
- }
- leaf dmi-service-name {
- type string;
- }
- leaf dmi-data-service-name {
- type string;
- }
- leaf dmi-model-service-name {
- type string;
- }
-
- list additional-properties {
- key "name";
- leaf name {
- type string;
- }
- leaf value {
- type string;
- }
- }
-
- list public-properties {
- key "name";
- leaf name {
- type string;
- }
- leaf value {
- type string;
- }
- }
-
- container state {
- leaf cm-handle-state {
- type string;
- }
-
- container lock-reason {
- uses LockReason;
- }
-
- leaf last-update-time {
- type string;
- }
-
- leaf data-sync-enabled {
- type boolean;
- default "false";
- }
-
- container datastores {
- uses Datastores;
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/cps-ri/src/main/resources/yangResourceCsvGenerator.py b/cps-ri/src/main/resources/yangResourceCsvGenerator.py
deleted file mode 100644
index 3a076d4378..0000000000
--- a/cps-ri/src/main/resources/yangResourceCsvGenerator.py
+++ /dev/null
@@ -1,66 +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=========================================================
-
-
-import csv
-import datetime
-import hashlib
-import sys
-import re
-
-yang_source = ''
-checksum = ''
-regexForModuleName = '(?<=module)(.*)(?={)'
-regexForRevision = '(?<=revision)(.*)(?={)'
-
-def main():
- for yang_source in sys.argv[1:]:
- checksum = hashlib.sha256(str(yang_source).encode()).hexdigest()
-
- with open('changelog/db/changes/data/yang-models/' + yang_source + '.yang', 'r') as content:
- dmiRegistry = content.read()
-
- try:
- latestRevision = re.search(regexForRevision, dmiRegistry).group(0).replace('\"','').strip()
- except:
- print("ERROR IN in yangResourceCsvGenerator.py: Unable to find revision for " + yang_source + '.yang')
-
- try:
- module_name = re.search(regexForModuleName, dmiRegistry).group(0).strip()
- except:
- print("ERROR IN in yangResourceCsvGenerator.py: Unable to find module name for " + yang_source + '.yang')
-
- #If true, file was created after module_name and revision columns were added to yang-resources table
- writeWithModuleNameAndRevision = yang_source != 'dmi-registry@2021-12-13'
-
- try:
- # open the file in the write mode
- with open('changelog/db/changes/data/dmi/generated-csv/generated_yang_resource_' + yang_source + '.csv', 'w', newline='') \
- as file:
- writer = csv.writer(file, delimiter='|')
- if(writeWithModuleNameAndRevision):
- writer.writerow(["name", "content", "checksum", "module_name", "revision"])
- writer.writerow([yang_source + '.yang', dmiRegistry, checksum, module_name, latestRevision])
- else:
- writer.writerow(["name", "content", "checksum"])
- writer.writerow([yang_source + '.yang', dmiRegistry, checksum])
- except:
- print("ERROR IN in yangResourceCsvGenerator.py: Unable to write to changelog/db/changes/data/dmi/generated-csv/generated_yang_resource_" + yang_source + ".csv")
-
-
-main() \ No newline at end of file
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy
index c72c3046e8..36bf55e2db 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy
@@ -18,29 +18,29 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.impl
+package org.onap.cps.ri
import com.fasterxml.jackson.databind.ObjectMapper
import org.hibernate.StaleStateException
+import org.onap.cps.ri.models.AnchorEntity
+import org.onap.cps.ri.models.DataspaceEntity
+import org.onap.cps.ri.models.FragmentEntity
+import org.onap.cps.ri.repository.AnchorRepository
+import org.onap.cps.ri.repository.DataspaceRepository
+import org.onap.cps.ri.repository.FragmentRepository
+import org.onap.cps.ri.utils.SessionManager
import org.onap.cps.spi.FetchDescendantsOption
-import org.onap.cps.spi.entities.AnchorEntity
-import org.onap.cps.spi.entities.DataspaceEntity
-import org.onap.cps.spi.entities.FragmentEntity
-
import org.onap.cps.spi.exceptions.ConcurrencyException
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.DataNodeBuilder
-import org.onap.cps.spi.repository.AnchorRepository
-import org.onap.cps.spi.repository.DataspaceRepository
-import org.onap.cps.spi.repository.FragmentRepository
-import org.onap.cps.spi.utils.SessionManager
import org.onap.cps.utils.JsonObjectMapper
import org.springframework.dao.DataIntegrityViolationException
import spock.lang.Specification
+
import java.util.stream.Collectors
-class CpsDataPersistenceServiceSpec extends Specification {
+class CpsDataPersistenceServiceImplSpec extends Specification {
def mockDataspaceRepository = Mock(DataspaceRepository)
def mockAnchorRepository = Mock(AnchorRepository)
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceConcurrencySpec.groovy
index 2e4dba2e9b..b892fe4dae 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceConcurrencySpec.groovy
@@ -18,19 +18,19 @@
* SPDX-License-Identifier: Apache-2.0
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.impl
+package org.onap.cps.ri
import org.hibernate.exception.ConstraintViolationException
+import org.onap.cps.ri.models.DataspaceEntity
+import org.onap.cps.ri.models.SchemaSetEntity
+import org.onap.cps.ri.repository.DataspaceRepository
+import org.onap.cps.ri.repository.ModuleReferenceRepository
+import org.onap.cps.ri.repository.SchemaSetRepository
+import org.onap.cps.ri.repository.YangResourceRepository
import org.onap.cps.spi.CpsAdminPersistenceService
import org.onap.cps.spi.CpsModulePersistenceService
-import org.onap.cps.spi.entities.DataspaceEntity
-import org.onap.cps.spi.entities.SchemaSetEntity
import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
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.onap.cps.spi.repository.YangResourceRepository
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceImplSpec.groovy
index 3447a1c599..1b61ff39c0 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceImplSpec.groovy
@@ -17,25 +17,26 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.impl
+package org.onap.cps.ri
import org.hibernate.exception.ConstraintViolationException
+import org.onap.cps.ri.models.SchemaSetEntity
+import org.onap.cps.ri.repository.DataspaceRepository
+import org.onap.cps.ri.repository.ModuleReferenceRepository
+import org.onap.cps.ri.repository.SchemaSetRepository
+import org.onap.cps.ri.repository.YangResourceRepository
import org.onap.cps.spi.CpsModulePersistenceService
-import org.onap.cps.spi.entities.SchemaSetEntity
import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
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.onap.cps.spi.repository.YangResourceRepository
import org.springframework.dao.DataIntegrityViolationException
import spock.lang.Specification
+
import java.sql.SQLException
/**
* Specification unit test class for CPS module persistence service.
*/
-class CpsModulePersistenceServiceSpec extends Specification {
+class CpsModulePersistenceServiceImplSpec extends Specification {
CpsModulePersistenceService objectUnderTest
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/utils/CpsValidatorSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/CpsValidatorImplSpec.groovy
index 8d348443c7..d57bf25058 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/utils/CpsValidatorSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/CpsValidatorImplSpec.groovy
@@ -18,13 +18,14 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.impl.utils
+package org.onap.cps.ri.utils
+
import org.onap.cps.spi.PaginationOption
import org.onap.cps.spi.exceptions.DataValidationException
import spock.lang.Specification
-class CpsValidatorSpec extends Specification {
+class CpsValidatorImplSpec extends Specification {
def objectUnderTest = new CpsValidatorImpl()
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/EscapeUtilsSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/EscapeUtilsSpec.groovy
index 52330e6251..8afd9695a7 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/EscapeUtilsSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/EscapeUtilsSpec.groovy
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.utils
+package org.onap.cps.ri.utils
import spock.lang.Specification
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/SessionManagerSpec.groovy
index feda338b80..b50a20c124 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/SessionManagerSpec.groovy
@@ -18,20 +18,18 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.utils
+package org.onap.cps.ri.utils
import com.google.common.util.concurrent.TimeLimiter
import com.google.common.util.concurrent.UncheckedExecutionException
import org.hibernate.HibernateException
+import org.hibernate.Session
import org.hibernate.Transaction
-import org.onap.cps.spi.config.CpsSessionFactory
-import org.onap.cps.spi.entities.AnchorEntity
+import org.onap.cps.ri.models.AnchorEntity
+import org.onap.cps.ri.repository.AnchorRepository
+import org.onap.cps.ri.repository.DataspaceRepository
import org.onap.cps.spi.exceptions.SessionManagerException
-import org.onap.cps.spi.repository.AnchorRepository
-import org.onap.cps.spi.repository.DataspaceRepository
import spock.lang.Specification
-import org.hibernate.Session
-import java.util.concurrent.ExecutionException
class SessionManagerSpec extends Specification {
diff --git a/cps-service/pom.xml b/cps-service/pom.xml
index 2564ceb989..c7a3666a07 100644
--- a/cps-service/pom.xml
+++ b/cps-service/pom.xml
@@ -29,7 +29,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
@@ -64,17 +64,17 @@
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
+ <!-- Disable SpotBug Rules -->
+ <dependency>
+ <groupId>com.github.spotbugs</groupId>
+ <artifactId>spotbugs-annotations</artifactId>
+ </dependency>
<dependency>
<!-- For parsing JSON object -->
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
- <!-- Hazelcast provide Distributed Caches -->
- <groupId>com.hazelcast</groupId>
- <artifactId>hazelcast-spring</artifactId>
- </dependency>
- <dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
@@ -144,18 +144,7 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
- <dependency>
- <groupId>io.cloudevents</groupId>
- <artifactId>cloudevents-json-jackson</artifactId>
- </dependency>
- <dependency>
- <groupId>io.cloudevents</groupId>
- <artifactId>cloudevents-kafka</artifactId>
- </dependency>
- <dependency>
- <groupId>io.cloudevents</groupId>
- <artifactId>cloudevents-spring</artifactId>
- </dependency>
+
<!-- T E S T D E P E N D E N C I E S -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
index f396b49e6b..b3eff8eb26 100644
--- a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
+++ b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
@@ -55,7 +55,7 @@ public interface CpsDataService {
* @param anchorName anchor name
* @param nodeData node data
* @param observedTimestamp observedTimestamp
- * @param contentType node data content type
+ * @param contentType JSON/XML content type
*/
void saveData(String dataspaceName, String anchorName, String nodeData, OffsetDateTime observedTimestamp,
ContentType contentType);
@@ -80,7 +80,7 @@ public interface CpsDataService {
* @param parentNodeXpath parent node xpath
* @param nodeData node data
* @param observedTimestamp observedTimestamp
- * @param contentType node data content type
+ * @param contentType JSON/XML content type
*
*/
void saveData(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData,
@@ -93,11 +93,12 @@ public interface CpsDataService {
* @param dataspaceName dataspace name
* @param anchorName anchor name
* @param parentNodeXpath parent node xpath
- * @param jsonData json data representing list element(s)
+ * @param nodeData node data representing list element(s)
* @param observedTimestamp observedTimestamp
+ * @param contentType JSON/XML content type
*/
- void saveListElements(String dataspaceName, String anchorName, String parentNodeXpath, String jsonData,
- OffsetDateTime observedTimestamp);
+ void saveListElements(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData,
+ OffsetDateTime observedTimestamp, ContentType contentType);
/**
* Retrieves all the datanodes by XPath for given dataspace and anchor.
@@ -134,7 +135,7 @@ public interface CpsDataService {
* @param parentNodeXpath xpath to parent node
* @param nodeData node data
* @param observedTimestamp observedTimestamp
- * @param contentType node data content type
+ * @param contentType JSON/XML content type
*/
void updateNodeLeaves(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData,
OffsetDateTime observedTimestamp, ContentType contentType);
@@ -145,22 +146,24 @@ public interface CpsDataService {
* @param dataspaceName dataspace name
* @param anchorName anchor name
* @param parentNodeXpath xpath to parent node
- * @param jsonData json data
+ * @param nodeData node data
* @param observedTimestamp observedTimestamp
+ * @param contentType JSON/XML content type
*/
- void updateDataNodeAndDescendants(String dataspaceName, String anchorName, String parentNodeXpath, String jsonData,
- OffsetDateTime observedTimestamp);
+ void updateDataNodeAndDescendants(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData,
+ OffsetDateTime observedTimestamp, ContentType contentType);
/**
* Replaces multiple existing data nodes' content including descendants in a batch operation.
*
* @param dataspaceName dataspace name
* @param anchorName anchor name
- * @param nodesJsonData map of xpath and node JSON data
+ * @param nodeDataPerXPath map of xpath and node JSON/XML data
* @param observedTimestamp observedTimestamp
+ * @param contentType JSON/XML content type
*/
- void updateDataNodesAndDescendants(String dataspaceName, String anchorName, Map<String, String> nodesJsonData,
- OffsetDateTime observedTimestamp);
+ void updateDataNodesAndDescendants(String dataspaceName, String anchorName, Map<String, String> nodeDataPerXPath,
+ OffsetDateTime observedTimestamp, ContentType contentType);
/**
* Replaces list content by removing all existing elements and inserting the given new elements as json
@@ -319,4 +322,18 @@ public interface CpsDataService {
Map<String, String> yangResourcesNameToContentMap,
String targetData,
FetchDescendantsOption fetchDescendantsOption);
+
+
+ /**
+ * Validates JSON or XML data by parsing it using the schema associated to an anchor within the given dataspace.
+ * Validation is performed without persisting the data.
+ *
+ * @param dataspaceName the name of the dataspace where the anchor is located.
+ * @param anchorName the name of the anchor used to validate the data.
+ * @param parentNodeXpath the xpath of the parent node where the data is to be validated.
+ * @param nodeData the JSON or XML data to be validated.
+ * @param contentType the content type of the data (e.g., JSON or XML).
+ */
+ void validateData(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData,
+ ContentType contentType);
}
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
index bdd361458e..931209c998 100644
--- a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
+++ b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
@@ -155,4 +155,37 @@ public interface CpsModuleService {
Collection<ModuleReference> identifyNewModuleReferences(
Collection<ModuleReference> moduleReferencesToCheck);
+ /**
+ * Retrieves module references based on the provided dataspace name, anchor name and attribute filters
+ * for both parent and child fragments.
+
+ * This method constructs and executes a SQL query to find module references from a database, using
+ * the specified `dataspaceName`, `anchorName` and two sets of attribute filters: one for parent fragments
+ * and one for child fragments. The method applies these filters to identify the appropriate fragments
+ * and schema sets, and then retrieves the corresponding module references.
+
+ * The SQL query is dynamically built based on the provided attribute filters:
+ * - The `parentAttributes` map is used to filter the parent fragments. The entries in this map are
+ * converted into a WHERE clause for the parent fragments.
+ * - The `childAttributes` map is used to filter the child fragments. This is applied to the child fragments
+ * after filtering the parent fragments.
+ *
+ * @param dataspaceName the name of the dataspace to filter on. It is used to locate the relevant dataspace
+ * in the database.
+ * @param anchorName the name of the anchor to filter on. It is used to locate the relevant anchor within
+ * the dataspace.
+ * @param parentAttributes a map of attributes to filter parent fragments. Each entry in this map represents
+ * an attribute key-value pair used in the WHERE clause for parent fragments.
+ * @param childAttributes a map of attributes to filter child fragments. Each entry in this map represents
+ * an attribute key-value pair used in the WHERE clause for child fragments.
+ * @return a collection of {@link ModuleReference} objects that match the given criteria. Each
+ * {@code ModuleReference} contains information about a module's name and revision.
+ * @implNote The method assumes that both `parentAttributes` and `childAttributes` maps contain at least
+ * one entry. The first entry from `parentAttributes` is used to filter parent fragments,
+ * and the first entry from `childAttributes` is used to filter child fragments.
+ */
+ Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName, final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes);
+
}
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java
index c31e51b174..5ca0fe63d4 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java
@@ -23,10 +23,10 @@ package org.onap.cps.api.impl;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import org.onap.cps.api.CpsAnchorService;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.spi.CpsAdminPersistenceService;
import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.model.Anchor;
-import org.onap.cps.spi.utils.CpsValidator;
import org.springframework.stereotype.Service;
@Service
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
index 5a48428772..b1b545be68 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
@@ -42,6 +42,7 @@ import org.onap.cps.api.CpsDeltaService;
import org.onap.cps.cpspath.parser.CpsPathUtil;
import org.onap.cps.events.CpsDataUpdateEventsService;
import org.onap.cps.events.model.Data.Operation;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.exceptions.DataValidationException;
@@ -49,7 +50,6 @@ import org.onap.cps.spi.model.Anchor;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.DataNodeBuilder;
import org.onap.cps.spi.model.DeltaReport;
-import org.onap.cps.spi.utils.CpsValidator;
import org.onap.cps.utils.ContentType;
import org.onap.cps.utils.DataMapUtils;
import org.onap.cps.utils.JsonObjectMapper;
@@ -64,6 +64,7 @@ import org.springframework.stereotype.Service;
public class CpsDataServiceImpl implements CpsDataService {
private static final String ROOT_NODE_XPATH = "/";
+ private static final String PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH = "";
private static final long DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS = 300L;
private static final String NO_DATA_NODES = "No data nodes.";
@@ -119,12 +120,13 @@ public class CpsDataServiceImpl implements CpsDataService {
@Override
@Timed(value = "cps.data.service.list.element.save",
description = "Time taken to save list elements")
- public void saveListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
- final String jsonData, final OffsetDateTime observedTimestamp) {
+ public void saveListElements(final String dataspaceName, final String anchorName,
+ final String parentNodeXpath, final String nodeData,
+ final OffsetDateTime observedTimestamp, final ContentType contentType) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> listElementDataNodeCollection =
- buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON);
+ buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType);
if (isRootNodeXpath(parentNodeXpath)) {
cpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName, listElementDataNodeCollection);
} else {
@@ -251,12 +253,12 @@ public class CpsDataServiceImpl implements CpsDataService {
@Timed(value = "cps.data.service.datanode.descendants.update",
description = "Time taken to update a data node and descendants")
public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName,
- final String parentNodeXpath, final String jsonData,
- final OffsetDateTime observedTimestamp) {
+ final String parentNodeXpath, final String nodeData,
+ final OffsetDateTime observedTimestamp, final ContentType contentType) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> dataNodes =
- buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON);
+ buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType);
cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
sendDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp);
}
@@ -265,13 +267,13 @@ public class CpsDataServiceImpl implements CpsDataService {
@Timed(value = "cps.data.service.datanode.descendants.batch.update",
description = "Time taken to update a batch of data nodes and descendants")
public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
- final Map<String, String> nodesJsonData,
- final OffsetDateTime observedTimestamp) {
+ final Map<String, String> nodeDataPerXPath,
+ final OffsetDateTime observedTimestamp, final ContentType contentType) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
- final Collection<DataNode> dataNodes = buildDataNodesWithParentNodeXpath(anchor, nodesJsonData);
+ final Collection<DataNode> dataNodes = buildDataNodesWithParentNodeXpath(anchor, nodeDataPerXPath, contentType);
cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
- nodesJsonData.keySet().forEach(nodeXpath ->
+ nodeDataPerXPath.keySet().forEach(nodeXpath ->
sendDataUpdatedEvent(anchor, nodeXpath, Operation.UPDATE, observedTimestamp));
}
@@ -357,6 +359,14 @@ public class CpsDataServiceImpl implements CpsDataService {
sendDataUpdatedEvent(anchor, listNodeXpath, Operation.DELETE, observedTimestamp);
}
+ @Override
+ public void validateData(final String dataspaceName, final String anchorName, final String parentNodeXpath,
+ final String nodeData, final ContentType contentType) {
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
+ final String xpath = ROOT_NODE_XPATH.equals(parentNodeXpath) ? PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH :
+ CpsPathUtil.getNormalizedXpath(parentNodeXpath);
+ yangParser.validateData(contentType, nodeData, anchor, xpath);
+ }
private Collection<DataNode> rebuildSourceDataNodes(final String xpath, final Anchor sourceAnchor,
final Collection<DataNode> sourceDataNodes) {
@@ -399,8 +409,7 @@ public class CpsDataServiceImpl implements CpsDataService {
private List<Map<String, Object>> prefixResolver(final Anchor anchor, final Collection<DataNode> dataNodes) {
final List<Map<String, Object>> prefixToDataNodes = new ArrayList<>(dataNodes.size());
for (final DataNode dataNode: dataNodes) {
- final String prefix = prefixResolver
- .getPrefix(anchor.getDataspaceName(), anchor.getName(), dataNode.getXpath());
+ final String prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath());
final Map<String, Object> prefixToDataNode = DataMapUtils.toDataMapWithIdentifier(dataNode, prefix);
prefixToDataNodes.add(prefixToDataNode);
}
@@ -408,11 +417,12 @@ public class CpsDataServiceImpl implements CpsDataService {
}
private Collection<DataNode> buildDataNodesWithParentNodeXpath(final Anchor anchor,
- final Map<String, String> nodesJsonData) {
+ final Map<String, String> nodesJsonData,
+ final ContentType contentType) {
final Collection<DataNode> dataNodes = new ArrayList<>();
for (final Map.Entry<String, String> nodeJsonData : nodesJsonData.entrySet()) {
dataNodes.addAll(buildDataNodesWithParentNodeXpath(anchor, nodeJsonData.getKey(),
- nodeJsonData.getValue(), ContentType.JSON));
+ nodeJsonData.getValue(), contentType));
}
return dataNodes;
}
@@ -421,7 +431,8 @@ public class CpsDataServiceImpl implements CpsDataService {
final String nodeData, final ContentType contentType) {
if (ROOT_NODE_XPATH.equals(parentNodeXpath)) {
- final ContainerNode containerNode = yangParser.parseData(contentType, nodeData, anchor, "");
+ final ContainerNode containerNode = yangParser.parseData(contentType, nodeData,
+ anchor, PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH);
final Collection<DataNode> dataNodes = new DataNodeBuilder()
.withContainerNode(containerNode)
.buildCollection();
@@ -449,7 +460,7 @@ public class CpsDataServiceImpl implements CpsDataService {
if (isRootNodeXpath(xpath)) {
final ContainerNode containerNode = yangParser.parseData(contentType, nodeData,
- yangResourcesNameToContentMap, "");
+ yangResourcesNameToContentMap, PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH);
final Collection<DataNode> dataNodes = new DataNodeBuilder()
.withContainerNode(containerNode)
.buildCollection();
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java
index a7f5da4874..6bccf2a865 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java
@@ -26,9 +26,9 @@ package org.onap.cps.api.impl;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import org.onap.cps.api.CpsDataspaceService;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.spi.CpsAdminPersistenceService;
import org.onap.cps.spi.model.Dataspace;
-import org.onap.cps.spi.utils.CpsValidator;
import org.springframework.stereotype.Service;
@Service
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java
index 2f99dbf7bb..7819568ae1 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java
@@ -20,7 +20,6 @@
package org.onap.cps.api.impl;
-
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
@@ -179,7 +178,7 @@ public class CpsDeltaServiceImpl implements CpsDeltaService {
final List<DeltaReport> updatedDeltaReportEntries) {
for (final Map.Entry<Map<String, Serializable>, Map<String, Serializable>> entry:
updatedLeavesAsSourceDataToTargetData.entrySet()) {
- final DeltaReport updatedDataForDeltaReport = new DeltaReportBuilder().actionUpdate()
+ final DeltaReport updatedDataForDeltaReport = new DeltaReportBuilder().actionReplace()
.withXpath(xpath).withSourceData(entry.getKey()).withTargetData(entry.getValue()).build();
updatedDeltaReportEntries.add(updatedDataForDeltaReport);
}
@@ -195,7 +194,7 @@ public class CpsDeltaServiceImpl implements CpsDeltaService {
for (final Map.Entry<String, DataNode> entry: xpathToAddedNodes.entrySet()) {
final String xpath = entry.getKey();
final DataNode dataNode = entry.getValue();
- final DeltaReport addedDataForDeltaReport = new DeltaReportBuilder().actionAdd().withXpath(xpath)
+ final DeltaReport addedDataForDeltaReport = new DeltaReportBuilder().actionCreate().withXpath(xpath)
.withTargetData(dataNode.getLeaves()).build();
addedDeltaReportEntries.add(addedDataForDeltaReport);
}
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
index e6ad9a8bb8..a600b22b61 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
@@ -30,6 +30,7 @@ import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsModuleService;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.spi.CascadeDeleteAllowed;
import org.onap.cps.spi.CpsModulePersistenceService;
import org.onap.cps.spi.exceptions.SchemaSetInUseException;
@@ -37,7 +38,6 @@ import org.onap.cps.spi.model.Anchor;
import org.onap.cps.spi.model.ModuleDefinition;
import org.onap.cps.spi.model.ModuleReference;
import org.onap.cps.spi.model.SchemaSet;
-import org.onap.cps.spi.utils.CpsValidator;
import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder;
import org.onap.cps.yang.YangTextSchemaSourceSet;
import org.springframework.stereotype.Service;
@@ -171,6 +171,17 @@ public class CpsModuleServiceImpl implements CpsModuleService {
return cpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck);
}
+ @Timed(value = "cps.module.service.module.reference.query",
+ description = "Time taken to query list of module references")
+ @Override
+ public Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName,
+ final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes) {
+ return cpsModulePersistenceService.getModuleReferencesByAttribute(dataspaceName, anchorName, parentAttributes,
+ childAttributes);
+ }
+
private boolean isCascadeDeleteProhibited(final CascadeDeleteAllowed cascadeDeleteAllowed) {
return CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED == cascadeDeleteAllowed;
}
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java
index 1d7a7ceeb0..d1c98986e6 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java
@@ -25,11 +25,11 @@ import io.micrometer.core.annotation.Timed;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import org.onap.cps.api.CpsQueryService;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.PaginationOption;
import org.onap.cps.spi.model.DataNode;
-import org.onap.cps.spi.utils.CpsValidator;
import org.springframework.stereotype.Service;
@Service
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java b/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java
index 4fdae5a307..8b85dfca32 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java
@@ -27,8 +27,8 @@ import io.micrometer.core.instrument.Metrics;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.RequiredArgsConstructor;
+import org.onap.cps.impl.utils.CpsValidator;
import org.onap.cps.spi.CpsModulePersistenceService;
-import org.onap.cps.spi.utils.CpsValidator;
import org.onap.cps.yang.YangTextSchemaSourceSet;
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder;
import org.springframework.cache.annotation.CacheConfig;
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
deleted file mode 100644
index efe19c6cb7..0000000000
--- a/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * ============LICENSE_START========================================================
- * Copyright (C) 2022-2023 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.cache;
-
-import com.hazelcast.config.MapConfig;
-import com.hazelcast.map.IMap;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Core infrastructure of the hazelcast distributed cache for anchor data config use cases.
- */
-@Configuration
-public class AnchorDataCacheConfig extends HazelcastCacheConfig {
-
- private static final MapConfig anchorDataCacheMapConfig = createMapConfig("anchorDataCacheMapConfig");
-
- /**
- * Distributed instance of anchor data cache that contains module prefix by anchor name as properties.
- *
- * @return configured map of anchor data cache
- */
- @Bean
- public IMap<String, AnchorDataCacheEntry> anchorDataCache() {
- return createHazelcastInstance("hazelCastInstanceCpsCore", anchorDataCacheMapConfig).getMap("anchorDataCache");
- }
-}
diff --git a/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheEntry.java b/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheEntry.java
deleted file mode 100644
index 41adbdd5dc..0000000000
--- a/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheEntry.java
+++ /dev/null
@@ -1,44 +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.cache;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-
-public class AnchorDataCacheEntry implements Serializable {
-
- private static final long serialVersionUID = 2111243947810370698L;
-
- private Map<String, Serializable> properties = new HashMap<>();
-
- public Object getProperty(final String propertyName) {
- return properties.get(propertyName);
- }
-
- public void setProperty(final String propertyName, final Serializable value) {
- properties.put(propertyName, value);
- }
-
- public boolean hasProperty(final String propertyName) {
- return properties.containsKey(propertyName);
- }
-}
diff --git a/cps-service/src/main/java/org/onap/cps/events/EventsPublisher.java b/cps-service/src/main/java/org/onap/cps/events/EventsPublisher.java
index 8023fbfb25..46384b5933 100644
--- a/cps-service/src/main/java/org/onap/cps/events/EventsPublisher.java
+++ b/cps-service/src/main/java/org/onap/cps/events/EventsPublisher.java
@@ -44,7 +44,7 @@ public class EventsPublisher<T> {
/**
* KafkaTemplate for legacy (non-cloud) events.
- * Note: Cloud events should be used. This will be addressed as part of https://jira.onap.org/browse/CPS-1717.
+ * Note: Cloud events should be used. This will be addressed as part of https://lf-onap.atlassian.net/browse/CPS-1717.
*/
private final KafkaTemplate<String, T> legacyKafkaEventTemplate;
@@ -73,7 +73,7 @@ public class EventsPublisher<T> {
/**
* Generic Event publisher.
- * Note: Cloud events should be used. This will be addressed as part of https://jira.onap.org/browse/CPS-1717.
+ * Note: Cloud events should be used. This will be addressed as part of https://lf-onap.atlassian.net/browse/CPS-1717.
*
* @param topicName valid topic name
* @param eventKey message key
diff --git a/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java b/cps-service/src/main/java/org/onap/cps/impl/utils/CpsValidator.java
index ceb75c09b2..fd677eb2d2 100644
--- a/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java
+++ b/cps-service/src/main/java/org/onap/cps/impl/utils/CpsValidator.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.spi.utils;
+package org.onap.cps.impl.utils;
import org.onap.cps.spi.PaginationOption;
diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
index eeaaa47991..793f38e4bc 100755
--- a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
@@ -153,4 +153,20 @@ public interface CpsModulePersistenceService {
Collection<ModuleReference> identifyNewModuleReferences(
Collection<ModuleReference> moduleReferencesToCheck);
+ /**
+ * Retrieves module references based on the specified dataspace, anchor, and attribute filters.
+
+ * Constructs and executes a SQL query to find module references by applying filters for parent and child fragments.
+ * Uses `parentAttributes` for filtering parent fragments and `childAttributes` for filtering child fragments.
+ *
+ * @param dataspaceName the name of the dataspace to filter on.
+ * @param anchorName the name of the anchor to filter on.
+ * @param parentAttributes a map of attributes for filtering parent fragments.
+ * @param childAttributes a map of attributes for filtering child fragments.
+ * @return a collection of {@link ModuleReference} objects matching the criteria.
+ */
+ Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName, final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes);
+
}
diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java b/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java
index 34715e70b9..c6270a41d2 100644
--- a/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java
@@ -32,9 +32,9 @@ import lombok.Setter;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DeltaReport {
- public static final String ADD_ACTION = "add";
+ public static final String CREATE_ACTION = "create";
public static final String REMOVE_ACTION = "remove";
- public static final String UPDATE_ACTION = "update";
+ public static final String REPLACE_ACTION = "replace";
DeltaReport() {}
diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java b/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java
index 1e151eeb2d..a7e7aae215 100644
--- a/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java
@@ -48,8 +48,8 @@ public class DeltaReportBuilder {
return this;
}
- public DeltaReportBuilder actionAdd() {
- this.action = DeltaReport.ADD_ACTION;
+ public DeltaReportBuilder actionCreate() {
+ this.action = DeltaReport.CREATE_ACTION;
return this;
}
@@ -58,8 +58,8 @@ public class DeltaReportBuilder {
return this;
}
- public DeltaReportBuilder actionUpdate() {
- this.action = DeltaReport.UPDATE_ACTION;
+ public DeltaReportBuilder actionReplace() {
+ this.action = DeltaReport.REPLACE_ACTION;
return this;
}
diff --git a/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java b/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java
index 35dc7347b2..c3097cc552 100644
--- a/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java
+++ b/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation.
+ * Copyright (C) 2022-2024 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,53 +20,25 @@
package org.onap.cps.utils;
-import com.hazelcast.map.IMap;
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
-import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.impl.YangTextSchemaSourceSetCache;
-import org.onap.cps.cache.AnchorDataCacheEntry;
import org.onap.cps.cpspath.parser.CpsPathPrefixType;
import org.onap.cps.cpspath.parser.CpsPathQuery;
import org.onap.cps.cpspath.parser.CpsPathUtil;
import org.onap.cps.spi.model.Anchor;
import org.onap.cps.yang.YangTextSchemaSourceSet;
-import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class PrefixResolver {
- private static final long ANCHOR_DATA_CACHE_TTL_SECS = TimeUnit.HOURS.toSeconds(1);
-
- private static final String CACHE_ENTRY_PROPERTY_NAME = "prefixPerContainerName";
-
- private final CpsAnchorService cpsAnchorService;
-
private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache;
- private final IMap<String, AnchorDataCacheEntry> anchorDataCache;
-
- /**
- * Get the module prefix for the given xpath for a dataspace and anchor name.
- *
- * @param dataspaceName the name of the dataspace
- * @param anchorName the name of the anchor the xpath belongs to
- * @param xpath the xpath to prefix a prefix for
- * @return the prefix of the module the top level element of given xpath
- */
- public String getPrefix(final String dataspaceName, final String anchorName, final String xpath) {
- final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
- return getPrefix(anchor, xpath);
- }
-
/**
* Get the module prefix for the given xpath under the given anchor.
*
@@ -75,57 +47,25 @@ public class PrefixResolver {
* @return the prefix of the module the top level element of given xpath
*/
public String getPrefix(final Anchor anchor, final String xpath) {
- final Map<String, String> prefixPerContainerName = getPrefixPerContainerName(anchor);
- return getPrefixForTopContainer(prefixPerContainerName, xpath);
- }
-
- private Map<String, String> getPrefixPerContainerName(final Anchor anchor) {
- if (anchorDataCache.containsKey(anchor.getName())) {
- final AnchorDataCacheEntry anchorDataCacheEntry = anchorDataCache.get(anchor.getName());
- if (anchorDataCacheEntry.hasProperty(CACHE_ENTRY_PROPERTY_NAME)) {
- return (Map) anchorDataCacheEntry.getProperty(CACHE_ENTRY_PROPERTY_NAME);
- }
- }
- return createAndCachePrefixPerContainerNameMap(anchor);
- }
-
- private String getPrefixForTopContainer(final Map<String, String> prefixPerContainerName,
- final String xpath) {
final CpsPathQuery cpsPathQuery = CpsPathUtil.getCpsPathQuery(xpath);
- if (cpsPathQuery.getCpsPathPrefixType() == CpsPathPrefixType.ABSOLUTE) {
- final String topLevelContainerName = cpsPathQuery.getContainerNames().get(0);
- if (prefixPerContainerName.containsKey(topLevelContainerName)) {
- return prefixPerContainerName.get(topLevelContainerName);
- }
+ if (cpsPathQuery.getCpsPathPrefixType() != CpsPathPrefixType.ABSOLUTE) {
+ return "";
}
- return "";
- }
+ final String topLevelContainerName = cpsPathQuery.getContainerNames().get(0);
- private Map<String, String> createAndCachePrefixPerContainerNameMap(final Anchor anchor) {
final YangTextSchemaSourceSet yangTextSchemaSourceSet =
- yangTextSchemaSourceSetCache.get(anchor.getDataspaceName(), anchor.getSchemaSetName());
+ yangTextSchemaSourceSetCache.get(anchor.getDataspaceName(), anchor.getSchemaSetName());
final SchemaContext schemaContext = yangTextSchemaSourceSet.getSchemaContext();
- final Map<QNameModule, String> prefixPerQNameModule = new HashMap<>(schemaContext.getModules().size());
- for (final Module module : schemaContext.getModules()) {
- prefixPerQNameModule.put(module.getQNameModule(), module.getPrefix());
- }
- final HashMap<String, String> prefixPerContainerName = new HashMap<>();
- for (final DataSchemaNode dataSchemaNode : schemaContext.getChildNodes()) {
- if (dataSchemaNode instanceof DataNodeContainer) {
- final String containerName = dataSchemaNode.getQName().getLocalName();
- final String prefix = prefixPerQNameModule.get(dataSchemaNode.getQName().getModule());
- prefixPerContainerName.put(containerName, prefix);
- }
- }
- cachePrefixPerContainerNameMap(anchor.getName(), prefixPerContainerName);
- return prefixPerContainerName;
- }
- private void cachePrefixPerContainerNameMap(final String anchorName,
- final Serializable prefixPerContainerName) {
- final AnchorDataCacheEntry anchorDataCacheEntry = new AnchorDataCacheEntry();
- anchorDataCacheEntry.setProperty(CACHE_ENTRY_PROPERTY_NAME, prefixPerContainerName);
- anchorDataCache.put(anchorName, anchorDataCacheEntry, ANCHOR_DATA_CACHE_TTL_SECS, TimeUnit.SECONDS);
+ return schemaContext.getChildNodes().stream()
+ .filter(DataNodeContainer.class::isInstance)
+ .map(SchemaNode::getQName)
+ .filter(qname -> qname.getLocalName().equals(topLevelContainerName))
+ .findFirst()
+ .map(QName::getModule)
+ .flatMap(schemaContext::findModule)
+ .map(Module::getPrefix)
+ .orElse("");
}
}
diff --git a/cps-service/src/main/java/org/onap/cps/utils/YangParser.java b/cps-service/src/main/java/org/onap/cps/utils/YangParser.java
index dc23c6bc4a..168e0999d5 100644
--- a/cps-service/src/main/java/org/onap/cps/utils/YangParser.java
+++ b/cps-service/src/main/java/org/onap/cps/utils/YangParser.java
@@ -21,6 +21,9 @@
package org.onap.cps.utils;
+import static org.onap.cps.utils.YangParserHelper.VALIDATE_AND_PARSE;
+import static org.onap.cps.utils.YangParserHelper.VALIDATE_ONLY;
+
import io.micrometer.core.annotation.Timed;
import java.util.Map;
import lombok.RequiredArgsConstructor;
@@ -57,11 +60,12 @@ public class YangParser {
final String parentNodeXpath) {
final SchemaContext schemaContext = getSchemaContext(anchor);
try {
- return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath);
+ return yangParserHelper
+ .parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_AND_PARSE);
} catch (final DataValidationException e) {
invalidateCache(anchor);
}
- return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath);
+ return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_AND_PARSE);
}
/**
@@ -78,7 +82,31 @@ public class YangParser {
final Map<String, String> yangResourcesNameToContentMap,
final String parentNodeXpath) {
final SchemaContext schemaContext = getSchemaContext(yangResourcesNameToContentMap);
- return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath);
+ return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_AND_PARSE);
+ }
+
+ /**
+ * Parses data to validate it, using the schema context for given anchor.
+ *
+ * @param anchor the anchor used for node data validation
+ * @param parentNodeXpath the xpath of the parent node
+ * @param nodeData JSON or XML data string to validate
+ * @param contentType the content type of the data (e.g., JSON or XML)
+ * @throws DataValidationException if validation fails
+ */
+ public void validateData(final ContentType contentType,
+ final String nodeData,
+ final Anchor anchor,
+ final String parentNodeXpath) {
+ final SchemaContext schemaContext = getSchemaContext(anchor);
+ try {
+ yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_ONLY);
+ } catch (final DataValidationException e) {
+ invalidateCache(anchor);
+ log.error("Data validation failed for anchor: {}, xpath: {}, details: {}", anchor, parentNodeXpath,
+ e.getMessage());
+ }
+ yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_ONLY);
}
private SchemaContext getSchemaContext(final Anchor anchor) {
diff --git a/cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java b/cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java
index 5cadd29368..5612945ea9 100644
--- a/cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java
+++ b/cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java
@@ -22,6 +22,7 @@ package org.onap.cps.utils;
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.JsonReader;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.io.StringReader;
import java.net.URISyntaxException;
@@ -29,6 +30,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLInputFactory;
@@ -70,6 +72,9 @@ public class YangParserHelper {
static final String DATA_ROOT_NODE_NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0";
static final String DATA_ROOT_NODE_TAG_NAME = "data";
+ static final String DATA_VALIDATION_FAILURE_MESSAGE = "Data Validation Failed";
+ static final boolean VALIDATE_ONLY = true;
+ static final boolean VALIDATE_AND_PARSE = false;
/**
* Parses data into NormalizedNode according to given schema context.
@@ -83,11 +88,20 @@ public class YangParserHelper {
public ContainerNode parseData(final ContentType contentType,
final String nodeData,
final SchemaContext schemaContext,
- final String parentNodeXpath) {
+ final String parentNodeXpath,
+ final boolean validateOnly) {
if (contentType == ContentType.JSON) {
- return parseJsonData(nodeData, schemaContext, parentNodeXpath);
+ final ContainerNode validatedAndParsedJson = parseJsonData(nodeData, schemaContext, parentNodeXpath);
+ if (validateOnly) {
+ return null;
+ }
+ return validatedAndParsedJson;
+ }
+ final NormalizedNodeResult normalizedNodeResult = parseXmlData(nodeData, schemaContext, parentNodeXpath);
+ if (validateOnly) {
+ return null;
}
- return parseXmlData(nodeData, schemaContext, parentNodeXpath);
+ return buildContainerNodeFormNormalizedNodeResult(normalizedNodeResult);
}
private ContainerNode parseJsonData(final String jsonData,
@@ -120,18 +134,15 @@ public class YangParserHelper {
try (jsonParserStream) {
jsonParserStream.parse(jsonReader);
- } catch (final IOException | JsonSyntaxException exception) {
- throw new DataValidationException(
- "Failed to parse json data: " + jsonData, exception.getMessage(), exception);
- } catch (final IllegalStateException | IllegalArgumentException exception) {
+ } catch (final IOException | JsonSyntaxException | IllegalStateException | IllegalArgumentException exception) {
throw new DataValidationException(
- "Failed to parse json data. Unsupported xpath or json data:" + jsonData, exception
- .getMessage(), exception);
+ DATA_VALIDATION_FAILURE_MESSAGE, "Failed to parse json data. " + exception.getMessage(), exception);
}
return dataContainerNodeBuilder.build();
}
- private ContainerNode parseXmlData(final String xmlData,
+ @SuppressFBWarnings(value = "DCN_NULLPOINTER_EXCEPTION", justification = "Problem originates in 3PP code")
+ private NormalizedNodeResult parseXmlData(final String xmlData,
final SchemaContext schemaContext,
final String parentNodeXpath) {
final XMLInputFactory factory = XMLInputFactory.newInstance();
@@ -168,12 +179,17 @@ public class YangParserHelper {
} catch (final XMLStreamException | URISyntaxException | IOException | SAXException | NullPointerException
| ParserConfigurationException | TransformerException exception) {
throw new DataValidationException(
- "Failed to parse xml data: " + xmlData, exception.getMessage(), exception);
+ DATA_VALIDATION_FAILURE_MESSAGE, "Failed to parse xml data: " + exception.getMessage(), exception);
}
+ return normalizedNodeResult;
+ }
+
+ private ContainerNode buildContainerNodeFormNormalizedNodeResult(final NormalizedNodeResult normalizedNodeResult) {
+
final DataContainerChild dataContainerChild =
- (DataContainerChild) getFirstChildXmlRoot(normalizedNodeResult.getResult());
+ (DataContainerChild) getFirstChildXmlRoot(normalizedNodeResult.getResult());
final YangInstanceIdentifier.NodeIdentifier nodeIdentifier =
- new YangInstanceIdentifier.NodeIdentifier(dataContainerChild.getIdentifier().getNodeType());
+ new YangInstanceIdentifier.NodeIdentifier(dataContainerChild.getIdentifier().getNodeType());
return Builders.containerBuilder().withChild(dataContainerChild).withNodeIdentifier(nodeIdentifier).build();
}
@@ -185,12 +201,12 @@ public class YangParserHelper {
private static Map<String, Object> getDataSchemaNodeAndIdentifiersByXpath(final String parentNodeXpath,
final SchemaContext schemaContext) {
- final String[] xpathNodeIdSequence = xpathToNodeIdSequence(parentNodeXpath);
+ final List<String> xpathNodeIdSequence = xpathToNodeIdSequence(parentNodeXpath);
return findDataSchemaNodeAndIdentifiersByXpathNodeIdSequence(xpathNodeIdSequence, schemaContext.getChildNodes(),
new ArrayList<>());
}
- private static String[] xpathToNodeIdSequence(final String xpath) {
+ private static List<String> xpathToNodeIdSequence(final String xpath) {
try {
return CpsPathUtil.getXpathNodeIdSequence(xpath);
} catch (final PathParsingException pathParsingException) {
@@ -200,17 +216,16 @@ public class YangParserHelper {
}
private static Map<String, Object> findDataSchemaNodeAndIdentifiersByXpathNodeIdSequence(
- final String[] xpathNodeIdSequence,
+ final List<String> xpathNodeIdSequence,
final Collection<? extends DataSchemaNode> dataSchemaNodes,
final Collection<QName> dataSchemaNodeIdentifiers) {
- final String currentXpathNodeId = xpathNodeIdSequence[0];
+ final String currentXpathNodeId = xpathNodeIdSequence.get(0);
final DataSchemaNode currentDataSchemaNode = dataSchemaNodes.stream()
.filter(dataSchemaNode -> currentXpathNodeId.equals(dataSchemaNode.getQName().getLocalName()))
.findFirst().orElseThrow(() -> schemaNodeNotFoundException(currentXpathNodeId));
dataSchemaNodeIdentifiers.add(currentDataSchemaNode.getQName());
- if (xpathNodeIdSequence.length <= 1) {
- final Map<String, Object> dataSchemaNodeAndIdentifiers =
- new HashMap<>();
+ if (xpathNodeIdSequence.size() <= 1) {
+ final Map<String, Object> dataSchemaNodeAndIdentifiers = new HashMap<>();
dataSchemaNodeAndIdentifiers.put("dataSchemaNode", currentDataSchemaNode);
dataSchemaNodeAndIdentifiers.put("dataSchemaNodeIdentifiers", dataSchemaNodeIdentifiers);
return dataSchemaNodeAndIdentifiers;
@@ -221,13 +236,11 @@ public class YangParserHelper {
((DataNodeContainer) currentDataSchemaNode).getChildNodes(),
dataSchemaNodeIdentifiers);
}
- throw schemaNodeNotFoundException(xpathNodeIdSequence[1]);
+ throw schemaNodeNotFoundException(xpathNodeIdSequence.get(1));
}
- private static String[] getNextLevelXpathNodeIdSequence(final String[] xpathNodeIdSequence) {
- final String[] nextXpathNodeIdSequence = new String[xpathNodeIdSequence.length - 1];
- System.arraycopy(xpathNodeIdSequence, 1, nextXpathNodeIdSequence, 0, nextXpathNodeIdSequence.length);
- return nextXpathNodeIdSequence;
+ private static List<String> getNextLevelXpathNodeIdSequence(final List<String> xpathNodeIdSequence) {
+ return xpathNodeIdSequence.subList(1, xpathNodeIdSequence.size());
}
private static DataValidationException schemaNodeNotFoundException(final String schemaNodeIdentifier) {
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy
index e58a5024b5..ccf943a470 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy
@@ -20,11 +20,11 @@
package org.onap.cps.api.impl
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.spi.CpsAdminPersistenceService
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException
import org.onap.cps.spi.model.Anchor
-import org.onap.cps.spi.utils.CpsValidator
import spock.lang.Specification
class CpsAnchorServiceImplSpec extends Specification {
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
index 4e5807ec84..8c208a1cf8 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
@@ -23,14 +23,15 @@
package org.onap.cps.api.impl
-import com.fasterxml.jackson.databind.ObjectMapper
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.core.read.ListAppender
+import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.TestUtils
import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDeltaService
import org.onap.cps.events.CpsDataUpdateEventsService
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.ConcurrencyException
@@ -40,12 +41,11 @@ import org.onap.cps.spi.exceptions.SessionManagerException
import org.onap.cps.spi.exceptions.SessionTimeoutException
import org.onap.cps.spi.model.Anchor
import org.onap.cps.spi.model.DataNodeBuilder
-import org.onap.cps.spi.utils.CpsValidator
import org.onap.cps.utils.ContentType
-import org.onap.cps.utils.YangParser
-import org.onap.cps.utils.YangParserHelper
import org.onap.cps.utils.JsonObjectMapper
import org.onap.cps.utils.PrefixResolver
+import org.onap.cps.utils.YangParser
+import org.onap.cps.utils.YangParserHelper
import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder
import org.onap.cps.yang.YangTextSchemaSourceSet
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
@@ -53,6 +53,7 @@ import org.slf4j.LoggerFactory
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import spock.lang.Shared
import spock.lang.Specification
+
import java.time.OffsetDateTime
import static org.onap.cps.events.model.Data.Operation.DELETE
@@ -132,8 +133,8 @@ class CpsDataServiceImplSpec extends Specification {
where: 'given parameters'
scenario | invalidData | contentType || expectedMessage
'no data nodes' | '{}' | ContentType.JSON || 'No data nodes'
- 'invalid json' | '{invalid json' | ContentType.JSON || 'Failed to parse json data'
- 'invalid xml' | '<invalid xml' | ContentType.XML || 'Failed to parse xml data'
+ 'invalid json' | '{invalid json' | ContentType.JSON || 'Data Validation Failed'
+ 'invalid xml' | '<invalid xml' | ContentType.XML || 'Data Validation Failed'
}
def 'Saving list element data fragment under Root node.'() {
@@ -141,7 +142,7 @@ class CpsDataServiceImplSpec extends Specification {
setupSchemaSetMocks('bookstore.yang')
when: 'save data method is invoked with list element json data'
def jsonData = '{"bookstore-address":[{"bookstore-name":"Easons","address":"Dublin,Ireland","postal-code":"D02HA21"}]}'
- objectUnderTest.saveListElements(dataspaceName, anchorName, '/', jsonData, observedTimestamp)
+ objectUnderTest.saveListElements(dataspaceName, anchorName, '/', jsonData, observedTimestamp, ContentType.JSON)
then: 'the persistence service method is invoked with correct parameters'
1 * mockCpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName,
{ dataNodeCollection ->
@@ -169,12 +170,11 @@ class CpsDataServiceImplSpec extends Specification {
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
}
- def 'Saving list element data fragment under existing node.'() {
+ def 'Saving list element data fragment under existing JSON/XML node.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
- when: 'save data method is invoked with list element json data'
- def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
- objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
+ when: 'save data method is invoked with list element data'
+ objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', data, observedTimestamp, contentType)
then: 'the persistence service method is invoked with correct parameters'
1 * mockCpsDataPersistenceService.addListElements(dataspaceName, anchorName, '/test-tree',
{ dataNodeCollection ->
@@ -187,16 +187,23 @@ class CpsDataServiceImplSpec extends Specification {
)
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
+ where:
+ data | contentType
+ '{"branch": [{"name": "A"}, {"name": "B"}]}' | ContentType.JSON
+ '<test-tree xmlns="org:onap:cps:test:test-tree"><branch><name>A</name></branch><branch><name>B</name></branch></test-tree>' | ContentType.XML
}
- def 'Saving empty list element data fragment.'() {
+ def 'Saving empty list element data fragment for JSON/XML data.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
when: 'save data method is invoked with an empty list'
- def jsonData = '{"branch": []}'
- objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
+ objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', data, observedTimestamp, contentType)
then: 'invalid data exception is thrown'
thrown(DataValidationException)
+ where:
+ data | contentType
+ '{"branch": []}' | ContentType.JSON
+ '<test-tree><branch></branch></test-tree>' | ContentType.XML
}
def 'Get all data nodes #scenario.'() {
@@ -362,11 +369,11 @@ class CpsDataServiceImplSpec extends Specification {
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
}
- def 'Replace data node using singular data node: #scenario.'() {
+ def 'Replace data node using singular JSON data node: #scenario.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
when: 'replace data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath'
- objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp)
+ objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp, ContentType.JSON)
then: 'the persistence service method is invoked with correct parameters'
1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
{ dataNode -> dataNode.xpath == expectedNodeXpath})
@@ -379,30 +386,63 @@ class CpsDataServiceImplSpec extends Specification {
'json list' | '/test-tree' | '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}' || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"]
}
- def 'Replace data node using multiple data nodes: #scenario.'() {
+ def 'Replace data node using singular XML data node: #scenario.'() {
+ given: 'schema set for given anchor and dataspace references test-tree model'
+ setupSchemaSetMocks('test-tree.yang')
+ when: 'replace data method is invoked with XML data #xmlData and parent node xpath #parentNodeXpath'
+ objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, xmlData, observedTimestamp, ContentType.XML)
+ then: 'the persistence service method is invoked with correct parameters'
+ 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
+ { dataNode -> dataNode.xpath == expectedNodeXpath })
+ and: 'the CpsValidator is called on the dataspaceName and AnchorName'
+ 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
+ where: 'following parameters were used'
+ scenario | parentNodeXpath | xmlData || expectedNodeXpath
+ 'level 2 node' | '/test-tree' | '<branch><name>Name</name></branch>' || ['/test-tree/branch[@name=\'Name\']']
+ 'xml list' | '/test-tree' | '<test-tree xmlns="org:onap:cps:test:test-tree"><branch><name>Name1</name></branch>' + '<branch><name>Name2</name></branch></test-tree>' || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"]
+ }
+
+ def 'Replace data node using multiple JSON data nodes: #scenario.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
when: 'replace data method is invoked with a map of xpaths and json data'
- objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodesJsonData, observedTimestamp)
+ objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodeDataPerXPath, observedTimestamp, ContentType.JSON)
then: 'the persistence service method is invoked with correct parameters'
1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
{ dataNode -> dataNode.xpath == expectedNodeXpath})
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
where: 'following parameters were used'
- scenario | nodesJsonData || expectedNodeXpath
+ scenario | nodeDataPerXPath || expectedNodeXpath
'top level node' | ['/' : '{"test-tree": {"branch": []}}', '/test-tree' : '{"branch": [{"name":"Name"}]}'] || ["/test-tree", "/test-tree/branch[@name='Name']"]
'level 2 node' | ['/test-tree' : '{"branch": [{"name":"Name"}]}', '/test-tree/branch[@name=\'Name\']':'{"nest":{"name":"nestName"}}'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"]
'json list' | ['/test-tree' : '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}'] || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"]
}
+ def 'Replace data node using multiple XML data nodes: #scenario.'() {
+ given: 'schema set for given anchor and dataspace references test-tree model'
+ setupSchemaSetMocks('test-tree.yang')
+ when: 'replace data method is invoked with a map of xpaths and XML data'
+ objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodeXmlDataPerXPath, observedTimestamp, ContentType.XML)
+ then: 'the persistence service method is invoked with correct parameters'
+ 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
+ { dataNode -> dataNode.xpath == expectedNodeXpath })
+ and: 'the CpsValidator is called on the dataspaceName and AnchorName'
+ 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
+ where: 'following parameters were used'
+ scenario | nodeXmlDataPerXPath || expectedNodeXpath
+ 'top level node' | ['/test-tree': '<branch><name>Name</name></branch>'] || ["/test-tree/branch[@name='Name']"]
+ 'level 2 node' | ['/test-tree': '<branch><name>Name</name></branch>', '/test-tree/branch[@name=\'Name\']': '<nest><name>nestName</name></nest>'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"]
+ 'xml list' | ['/test-tree': '<test-tree xmlns="org:onap:cps:test:test-tree"><branch><name>Name1</name></branch>' + '<branch><name>Name2</name></branch></test-tree>'] || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"]
+ }
+
def 'Replace data node with concurrency exception in persistence layer.'() {
given: 'the persistence layer throws an concurrency exception'
def originalException = new ConcurrencyException('message', 'details')
mockCpsDataPersistenceService.updateDataNodesAndDescendants(*_) >> { throw originalException }
setupSchemaSetMocks('test-tree.yang')
when: 'attempt to replace data node'
- objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, ['/' : '{"test-tree": {}}'] , observedTimestamp)
+ objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, ['/' : '{"test-tree": {}}'] , observedTimestamp, ContentType.JSON)
then: 'the same exception is thrown up'
def thrownUp = thrown(ConcurrencyException)
assert thrownUp == originalException
@@ -506,6 +546,20 @@ class CpsDataServiceImplSpec extends Specification {
1 * mockDataUpdateEventsService.publishCpsDataUpdateEvent(anchor2, '/', DELETE, observedTimestamp)
}
+ def "Validating #scenario when dry run is enabled."() {
+ given: 'schema set for given anchors and dataspace references bookstore model'
+ setupSchemaSetMocks('bookstore.yang')
+ when: 'validating the data with the given parameters'
+ objectUnderTest.validateData(dataspaceName, anchorName, parentNodeXpath, data,contentType)
+ then: 'the appropriate yang parser method is invoked with correct parameters'
+ yangParser.validateData(contentType, data, anchor, xpath)
+ where: 'the following parameters were used'
+ scenario | parentNodeXpath | xpath | contentType | data
+ 'JSON data with root node xpath' | '/' | '' | ContentType.JSON | '{"bookstore":{"bookstore-name":"Easons"}}'
+ 'JSON data with specific xpath' | '/bookstore' | '/bookstore' | ContentType.JSON | '{"bookstore-name":"Easons"}'
+ 'XML data with specific xpath' | '/bookstore' | '/bookstore' | ContentType.XML | '<bookstore-name>Easons</bookstore-name>'
+ }
+
def 'Start session.'() {
when: 'start session method is called'
objectUnderTest.startSession()
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy
index 8e17594bd1..ac7d4c0aa7 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy
@@ -20,9 +20,9 @@
package org.onap.cps.api.impl
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.spi.CpsAdminPersistenceService
import org.onap.cps.spi.model.Dataspace
-import org.onap.cps.spi.utils.CpsValidator
import spock.lang.Specification
class CpsDataspaceServiceImplSpec extends Specification {
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy
index 42d75f3eab..f12afe61ec 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy
@@ -51,8 +51,8 @@ class CpsDeltaServiceImplSpec extends Specification{
def 'Get delta between data nodes for ADDED data'() {
when: 'attempt to get delta between 2 data nodes'
def result = objectUnderTest.getDeltaReports([], targetDataNodeWithLeafData)
- then: 'the delta report contains expected "add" action'
- assert result[0].action.equals('add')
+ then: 'the delta report contains expected "create" action'
+ assert result[0].action.equals('create')
and: 'the delta report contains expected xpath'
assert result[0].xpath == '/parent'
and: 'the delta report contains no source data'
@@ -68,12 +68,12 @@ class CpsDeltaServiceImplSpec extends Specification{
when: 'attempt to get delta between 2 data nodes'
def result = objectUnderTest.getDeltaReports(sourceDataNode, targetDataNode)
then: 'the delta report contains expected details for parent node'
- assert result[0].action.equals('update')
+ assert result[0].action.equals('replace')
assert result[0].xpath == '/parent'
assert result[0].sourceData == ['parent-leaf': 'parent-payload']
assert result[0].targetData == ['parent-leaf': 'parent-payload-updated']
and: 'the delta report contains expected details for child node'
- assert result[1].action.equals('update')
+ assert result[1].action.equals('replace')
assert result[1].xpath == '/parent/child'
assert result[1].sourceData == ['child-leaf': 'child-payload']
assert result[1].targetData == ['child-leaf': 'child-payload-updated']
@@ -82,8 +82,8 @@ class CpsDeltaServiceImplSpec extends Specification{
def 'Delta report between leaves, #scenario'() {
when: 'attempt to get delta between 2 data nodes'
def result = objectUnderTest.getDeltaReports(sourceDataNode, targetDataNode)
- then: 'the delta report contains expected "update" action'
- assert result[0].action.equals('update')
+ then: 'the delta report contains expected "replace" action'
+ assert result[0].action.equals('replace')
and: 'the delta report contains expected xpath'
assert result[0].xpath == '/parent'
and: 'the delta report contains expected source and target data'
@@ -100,7 +100,7 @@ class CpsDeltaServiceImplSpec extends Specification{
def 'Get delta between data nodes for updated data, where source and target data nodes have no leaves '() {
when: 'attempt to get delta between 2 data nodes'
def result = objectUnderTest.getDeltaReports(sourceDataNodeWithoutLeafData, targetDataNodeWithoutLeafData)
- then: 'the delta report contains "update" action with right data'
+ then: 'the delta report is empty'
assert result.isEmpty()
}
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
index ad8c54bf27..1831506563 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
@@ -23,19 +23,15 @@
package org.onap.cps.api.impl
-import org.onap.cps.api.CpsAnchorService
-
-import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
-import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED
-
import org.onap.cps.TestUtils
+import org.onap.cps.api.CpsAnchorService
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
import org.onap.cps.spi.exceptions.ModelValidationException
import org.onap.cps.spi.exceptions.SchemaSetInUseException
-import org.onap.cps.spi.model.ModuleDefinition
-import org.onap.cps.spi.utils.CpsValidator
import org.onap.cps.spi.model.Anchor
+import org.onap.cps.spi.model.ModuleDefinition
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.spi.model.SchemaSet
import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder
@@ -43,6 +39,9 @@ import org.onap.cps.yang.YangTextSchemaSourceSet
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import spock.lang.Specification
+import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
+import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED
+
class CpsModuleServiceImplSpec extends Specification {
def mockCpsModulePersistenceService = Mock(CpsModulePersistenceService)
@@ -238,6 +237,23 @@ class CpsModuleServiceImplSpec extends Specification {
1 * mockCpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck)
}
+ def 'Get module references when queried by attributes'() {
+ given: 'a valid dataspace name and anchor name'
+ def dataspaceName = 'someDataspace'
+ def anchorName = 'someAnchor'
+ and: 'a set of parent attributes and child attributes used for filtering'
+ def parentAttributes = ['some-property-key1': 'some-property-val1']
+ def childAttributes = ['some-property-key2': 'some-property-val2']
+ and: 'a list of expected module references returned by the persistence service'
+ def expectedModuleReferences = [new ModuleReference(moduleName: 'some-name', revision: 'some-revision')]
+ mockCpsModulePersistenceService.getModuleReferencesByAttribute(dataspaceName, anchorName, parentAttributes, childAttributes) >> expectedModuleReferences
+ when: 'the method is invoked to retrieve module references by attributes'
+ def actualModuleReferences = objectUnderTest.getModuleReferencesByAttribute(dataspaceName, anchorName, parentAttributes, childAttributes)
+ then: 'the retrieved module references should match the expected module references'
+ assert actualModuleReferences == expectedModuleReferences
+ }
+
+
def 'Getting module definitions with module name'() {
given: 'module persistence service returns module definitions for module name'
def moduleDefinitionsFromPersistenceService = [ new ModuleDefinition('name', 'revision', 'content' ) ]
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy
index 1ad5017919..3b10669ddb 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy
@@ -21,10 +21,10 @@
package org.onap.cps.api.impl
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.PaginationOption
-import org.onap.cps.spi.utils.CpsValidator
import spock.lang.Specification
class CpsQueryServiceImplSpec extends Specification {
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy
index 9e55e8f10a..9f3456280e 100755
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy
@@ -28,13 +28,13 @@ import org.onap.cps.TestUtils
import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDeltaService
import org.onap.cps.events.CpsDataUpdateEventsService
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.model.Anchor
-import org.onap.cps.spi.utils.CpsValidator
+import org.onap.cps.utils.ContentType
import org.onap.cps.utils.JsonObjectMapper
import org.onap.cps.utils.PrefixResolver
-import org.onap.cps.utils.ContentType
import org.onap.cps.utils.YangParser
import org.onap.cps.utils.YangParserHelper
import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder
@@ -171,6 +171,6 @@ class E2ENetworkSliceSpec extends Specification {
expect: 'schema context is built with no exception indicating the schema set being valid '
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap).getSchemaContext()
and: 'data is parsed with no exception indicating the model match'
- new YangParserHelper().parseData(ContentType.JSON, jsonData, schemaContext, '') != null
+ new YangParserHelper().parseData(ContentType.JSON, jsonData, schemaContext, '', false) != null
}
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy
index a9f50ee5b0..189e28521b 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy
@@ -22,6 +22,7 @@
package org.onap.cps.api.impl
import org.onap.cps.TestUtils
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.yang.YangTextSchemaSourceSet
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
@@ -34,8 +35,6 @@ import org.springframework.cache.annotation.EnableCaching
import org.springframework.cache.caffeine.CaffeineCacheManager
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
-import org.onap.cps.spi.utils.CpsValidator
-
@SpringBootTest
@EnableCaching
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
deleted file mode 100644
index 010308c42f..0000000000
--- a/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.cache
-
-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 org.springframework.test.context.ContextConfiguration
-import spock.lang.Specification
-
-@SpringBootTest
-@ContextConfiguration(classes = [AnchorDataCacheConfig])
-class AnchorDataCacheConfigSpec extends Specification {
-
- @Autowired
- private IMap<String, AnchorDataCacheEntry> anchorDataCache
-
- def 'Embedded (hazelcast) cache for Anchor Data.'() {
- expect: 'system is able to create an instance of the Anchor data cache'
- assert null != anchorDataCache
- and: 'there is at least 1 instance'
- assert Hazelcast.allHazelcastInstances.size() > 0
- and: 'anchorDataCache is present'
- assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceCpsCore')
- }
-
- def 'Verify configs for Distributed Caches'(){
- given: 'the Anchor Data Cache config'
- def anchorDataCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelCastInstanceCpsCore').config
- def anchorDataCacheMapConfig = anchorDataCacheConfig.mapConfigs.get('anchorDataCacheMapConfig')
- expect: 'system created instance with correct config'
- assert anchorDataCacheConfig.clusterName == 'cps-and-ncmp-test-caches'
- assert anchorDataCacheMapConfig.backupCount == 1
- assert anchorDataCacheMapConfig.asyncBackupCount == 0
- }
-
- 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/groovy/org/onap/cps/cache/AnchorDataCacheEntrySpec.groovy b/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheEntrySpec.groovy
deleted file mode 100644
index f38b45d17c..0000000000
--- a/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheEntrySpec.groovy
+++ /dev/null
@@ -1,40 +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.cache
-
-
-import spock.lang.Specification
-
-class AnchorDataCacheEntrySpec extends Specification {
-
- def objectUnderTest = new AnchorDataCacheEntry()
-
- def 'Anchor Data Cache Properties Management.'() {
- when: 'a property named "sample" is added to the cache'
- objectUnderTest.setProperty('sample', 123)
- then: 'the cache has that property'
- assert objectUnderTest.hasProperty('sample')
- and: 'the value is correct'
- assert objectUnderTest.getProperty('sample') == 123
- and: 'the cache does not have an an object called "something else"'
- assert objectUnderTest.hasProperty('something else') == false
- }
-}
diff --git a/cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy
index e305abee86..f028d5d5d9 100644
--- a/cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy
@@ -35,6 +35,7 @@ class DataNodeBuilderSpec extends Specification {
def objectUnderTest = new DataNodeBuilder()
def yangParserHelper = new YangParserHelper()
+ def validateAndParse = false
def expectedLeavesByXpathMap = [
'/test-tree' : [],
@@ -60,7 +61,7 @@ class DataNodeBuilderSpec extends Specification {
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext()
and: 'the json data parsed into container node object'
def jsonData = TestUtils.getResourceFileContent('test-tree.json')
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '')
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '', validateAndParse)
when: 'the container node is converted to a data node'
def result = objectUnderTest.withContainerNode(containerNode).build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
@@ -80,7 +81,7 @@ class DataNodeBuilderSpec extends Specification {
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext()
and: 'the json data parsed into container node object'
def jsonData = '{ "branch": [{ "name": "Branch", "nest": { "name": "Nest", "birds": ["bird"] } }] }'
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '/test-tree')
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '/test-tree', validateAndParse)
when: 'the container node is converted to a data node with parent node xpath defined'
def result = objectUnderTest.withContainerNode(containerNode).withParentNodeXpath('/test-tree').build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
@@ -96,7 +97,7 @@ class DataNodeBuilderSpec extends Specification {
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext()
and: 'the json data parsed into container node object'
def jsonData = TestUtils.getResourceFileContent('ietf/data/ietf-network-topology-sample-rfc8345.json')
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '')
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '', validateAndParse)
when: 'the container node is converted to a data node '
def result = objectUnderTest.withContainerNode(containerNode).build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
@@ -129,7 +130,7 @@ class DataNodeBuilderSpec extends Specification {
def parentNodeXpath = "/networks/network[@network-id='otn-hc']/link[@link-id='D1,1-2-1,D2,2-1-1']"
and: 'the json data fragment parsed into container node object for given parent node xpath'
def jsonData = '{"source": {"source-node": "D1", "source-tp": "1-2-1"}}'
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext,parentNodeXpath)
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext,parentNodeXpath, validateAndParse)
when: 'the container node is converted to a data node with given parent node xpath'
def result = objectUnderTest.withContainerNode(containerNode).withParentNodeXpath(parentNodeXpath).build()
then: 'the resulting data node represents a child of augmentation node'
@@ -144,7 +145,7 @@ class DataNodeBuilderSpec extends Specification {
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext()
and: 'the json data fragment parsed into container node object'
def jsonData = TestUtils.getResourceFileContent('data-with-choice-node.json')
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '')
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '', validateAndParse)
when: 'the container node is converted to a data node'
def result = objectUnderTest.withContainerNode(containerNode).build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
@@ -162,7 +163,7 @@ class DataNodeBuilderSpec extends Specification {
and: 'parent node xpath referencing parent of list element'
def parentNodeXpath = '/test-tree'
and: 'the json data fragment (list element) parsed into container node object'
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, parentNodeXpath)
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, parentNodeXpath, validateAndParse)
when: 'the container node is converted to a data node collection'
def result = objectUnderTest.withContainerNode(containerNode).withParentNodeXpath(parentNodeXpath).buildCollection()
def resultXpaths = result.collect { it.getXpath() }
diff --git a/cps-service/src/test/groovy/org/onap/cps/spi/model/DeltaReportBuilderSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/spi/model/DeltaReportBuilderSpec.groovy
index e19d120421..00b5499c2f 100644
--- a/cps-service/src/test/groovy/org/onap/cps/spi/model/DeltaReportBuilderSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/spi/model/DeltaReportBuilderSpec.groovy
@@ -24,20 +24,20 @@ import spock.lang.Specification
class DeltaReportBuilderSpec extends Specification{
- def 'Generating delta report with for add action'() {
+ def 'Generating delta report for "create" action'() {
when: 'delta report is generated'
def result = new DeltaReportBuilder()
- .actionAdd()
+ .actionCreate()
.withXpath('/xpath')
.withTargetData(['data':'leaf-data'])
.build()
- then: 'the delta report contains the "add" action with expected target data'
- assert result.action == 'add'
+ then: 'the delta report contains the "create" action with expected target data'
+ assert result.action == 'create'
assert result.xpath == '/xpath'
assert result.targetData == ['data': 'leaf-data']
}
- def 'Generating delta report with attributes for remove action'() {
+ def 'Generating delta report with attributes for "remove" action'() {
when: 'delta report is generated'
def result = new DeltaReportBuilder()
.actionRemove()
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy
index 8cbd493550..09d45b92c0 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy
@@ -42,7 +42,7 @@ class JsonObjectMapperSpec extends Specification {
def contentMap = new JsonSlurper().parseText(new String(content))
and: 'the parsed content is as expected'
assert contentMap.'test:bookstore'.'bookstore-name' == 'Chapters/Easons'
- where: 'the following data stores are used'
+ where: 'the following content types are used'
type << ['String', 'bytes']
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy
index b975de6555..13b042f1ae 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2021-2022 Bell Canada.
* ================================================================================
@@ -22,11 +22,8 @@
package org.onap.cps.utils
-import com.hazelcast.map.IMap
import org.onap.cps.TestUtils
-import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.impl.YangTextSchemaSourceSetCache
-import org.onap.cps.cache.AnchorDataCacheEntry
import org.onap.cps.spi.model.Anchor
import org.onap.cps.yang.YangTextSchemaSourceSet
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
@@ -34,13 +31,9 @@ import spock.lang.Specification
class PrefixResolverSpec extends Specification {
- def mockCpsAnchorService = Mock(CpsAnchorService)
-
def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
- def mockAnchorDataCache = Mock(IMap<String, AnchorDataCacheEntry>)
-
- def objectUnderTest = new PrefixResolver(mockCpsAnchorService, mockYangTextSchemaSourceSetCache, mockAnchorDataCache)
+ def objectUnderTest = new PrefixResolver(mockYangTextSchemaSourceSetCache)
def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
@@ -48,29 +41,16 @@ class PrefixResolverSpec extends Specification {
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
- def setup() {
- given: 'an anchor for the test-tree model'
- def anchor = new Anchor(dataspaceName: 'testDataspace', name: 'testAnchor')
- and: 'the system can get this anchor'
- mockCpsAnchorService.getAnchor('testDataspace', 'testAnchor') >> anchor
- and: 'the schema source cache contains the schema context for the test-tree module'
- mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
- }
+ def anchor = new Anchor(dataspaceName: 'testDataspace', name: 'testAnchor')
def 'get xpath prefix using node schema context'() {
+ given: 'the schema source cache contains the schema context for the test-tree module'
+ mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
+ mockYangTextSchemaSourceSetCache.get(*_) >> mockYangTextSchemaSourceSet
when: 'the prefix of the yang module is retrieved'
- def result = objectUnderTest.getPrefix('testDataspace', 'testAnchor', xpath)
+ def result = objectUnderTest.getPrefix(anchor, xpath)
then: 'the expected prefix is returned'
result == expectedPrefix
- and: 'the cache is updated for the given anchor with a map of prefixes per top level container (just one one this case)'
- 1 * mockAnchorDataCache.put('testAnchor',_ , _ ,_) >> { args -> {
- def prefixPerContainerName = args[1].getProperty("prefixPerContainerName")
- assert prefixPerContainerName.size() == 1
- assert prefixPerContainerName.get('test-tree') == 'tree'
- }
- }
- and: 'schema source cache is used (i.e. need to build schema context)'
- 1 * mockYangTextSchemaSourceSetCache.get(*_) >> mockYangTextSchemaSourceSet
where: 'the following scenarios are applied'
xpath || expectedPrefix
'/test-tree' || 'tree'
@@ -82,37 +62,4 @@ class PrefixResolverSpec extends Specification {
'/not-defined' || ''
}
- def 'get prefix with populated anchor data cache with #scenario cache entry'() {
- given: 'anchor data cache is populated for the anchor with a prefix for top level container named #cachedTopLevelContainerName'
- def anchorDataCacheEntry = new AnchorDataCacheEntry()
- def prefixPerContainerName = [(cachedTopLevelContainerName): 'cachedPrefix']
- anchorDataCacheEntry.setProperty('prefixPerContainerName',prefixPerContainerName)
- mockAnchorDataCache.containsKey('testAnchor') >> true
- mockAnchorDataCache.get('testAnchor') >> anchorDataCacheEntry
- when: 'the prefix of the yang module is retrieved'
- def result = objectUnderTest.getPrefix('testDataspace', 'testAnchor', '/test-tree')
- then: 'the expected prefix is returned'
- result == expectedPrefix
- and: 'schema source cache is not used (i.e. no need to build schema context)'
- 0 * mockYangTextSchemaSourceSetCache.get(*_)
- where: 'the following scenarios are applied'
- scenario | cachedTopLevelContainerName || expectedPrefix
- 'matching' | 'test-tree' || 'cachedPrefix'
- 'non-matching' | 'other' || ''
- }
-
- def 'get prefix with other (non relevant) data in anchor data cache'() {
- given: 'anchor data cache is populated with non relevant other property'
- def anchorDataCacheEntry = new AnchorDataCacheEntry()
- anchorDataCacheEntry.setProperty('something else', 'does not matter')
- mockAnchorDataCache.containsKey('testAnchor') >> true
- mockAnchorDataCache.get('testAnchor') >> anchorDataCacheEntry
- when: 'the prefix of the yang module is retrieved'
- def result = objectUnderTest.getPrefix('testDataspace', 'testAnchor', '/test-tree')
- then: 'the expected prefix is returned'
- result == 'tree'
- and: 'schema source cache is used (i.e. need to build schema context)'
- 1 * mockYangTextSchemaSourceSetCache.get(*_) >> mockYangTextSchemaSourceSet
- }
-
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy
index 073383113d..e1490c28ab 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy
@@ -30,6 +30,8 @@ import spock.lang.Specification
class YangParserHelperSpec extends Specification {
def objectUnderTest = new YangParserHelper()
+ def validateOnly = true
+ def validateAndParse = false
def 'Parsing a valid multicontainer Json String.'() {
given: 'a yang model (file)'
@@ -38,7 +40,7 @@ class YangParserHelperSpec extends Specification {
def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('multipleDataTree.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
when: 'the json data is parsed'
- def result = objectUnderTest.parseData(ContentType.JSON, jsonData, schemaContext, '')
+ def result = objectUnderTest.parseData(ContentType.JSON, jsonData, schemaContext, '', validateAndParse)
then: 'a ContainerNode holding collection of normalized nodes is returned'
result.body().getAt(index) instanceof NormalizedNode == true
then: 'qualified name of children created is as expected'
@@ -56,7 +58,7 @@ class YangParserHelperSpec extends Specification {
def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
when: 'the data is parsed'
- NormalizedNode result = objectUnderTest.parseData(contentType, fileData, schemaContext, '')
+ NormalizedNode result = objectUnderTest.parseData(contentType, fileData, schemaContext, '', validateAndParse)
then: 'the result is a normalized node of the correct type'
if (revision) {
result.identifier.nodeType == QName.create(namespace, revision, localName)
@@ -74,7 +76,7 @@ class YangParserHelperSpec extends Specification {
def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
when: 'invalid data is parsed'
- objectUnderTest.parseData(contentType, invalidData, schemaContext, '')
+ objectUnderTest.parseData(contentType, invalidData, schemaContext, '', validateAndParse)
then: 'an exception is thrown'
thrown(DataValidationException)
where: 'the following invalid data is provided'
@@ -92,7 +94,7 @@ class YangParserHelperSpec extends Specification {
def yangResourcesMap = TestUtils.getYangResourcesAsMap('test-tree.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext()
when: 'json string is parsed'
- def result = objectUnderTest.parseData(contentType, nodeData, schemaContext, parentNodeXpath)
+ def result = objectUnderTest.parseData(contentType, nodeData, schemaContext, parentNodeXpath, validateAndParse)
then: 'a ContainerNode holding collection of normalized nodes is returned'
result.body().getAt(0) instanceof NormalizedNode == true
then: 'result represents a node of expected type'
@@ -112,7 +114,7 @@ class YangParserHelperSpec extends Specification {
def yangResourcesMap = TestUtils.getYangResourcesAsMap('test-tree.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext()
when: 'json string is parsed'
- objectUnderTest.parseData(ContentType.JSON, '{"nest": {"name" : "Nest", "birds": ["bird"]}}', schemaContext, parentNodeXpath)
+ objectUnderTest.parseData(ContentType.JSON, '{"nest": {"name" : "Nest", "birds": ["bird"]}}', schemaContext, parentNodeXpath, validateAndParse)
then: 'expected exception is thrown'
thrown(DataValidationException)
where:
@@ -129,7 +131,7 @@ class YangParserHelperSpec extends Specification {
def yangResourcesMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext()
when: 'malformed json string is parsed'
- objectUnderTest.parseData(ContentType.JSON, invalidJson, schemaContext, '')
+ objectUnderTest.parseData(ContentType.JSON, invalidJson, schemaContext, '', validateAndParse)
then: 'an exception is thrown'
thrown(DataValidationException)
where: 'the following malformed json is provided'
@@ -145,7 +147,7 @@ class YangParserHelperSpec extends Specification {
and: 'some json data with space in the array elements'
def jsonDataWithSpacesInArrayElement = TestUtils.getResourceFileContent('bookstore.json')
when: 'that json data is parsed'
- objectUnderTest.parseData(ContentType.JSON, jsonDataWithSpacesInArrayElement, schemaContext, '')
+ objectUnderTest.parseData(ContentType.JSON, jsonDataWithSpacesInArrayElement, schemaContext, '', validateAndParse)
then: 'no exception thrown'
noExceptionThrown()
}
@@ -162,5 +164,22 @@ class YangParserHelperSpec extends Specification {
'xpath contains list attributes with /' | '/test-tree/branch[@name=\'/Branch\']/categories[@id=\'/broken\']' || ['test-tree','branch','categories']
}
+ def 'Validating #scenario xpath String.'() {
+ given: 'a data model (file) is provided'
+ def fileData = TestUtils.getResourceFileContent(contentFile)
+ and: 'the schema context is built for that data model'
+ def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang')
+ def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
+ when: 'the data is parsed to be validated'
+ objectUnderTest.parseData(contentType, fileData, schemaContext, parentNodeXpath, validateOnly)
+ then: 'no exception is thrown'
+ noExceptionThrown()
+ where:
+ scenario | parentNodeXpath | contentFile | contentType
+ 'JSON without parent node' | '' | 'bookstore.json' | ContentType.JSON
+ 'JSON with parent node' | '/bookstore' | 'bookstore-categories-data.json' | ContentType.JSON
+ 'XML without parent node' | '' | 'bookstore.xml' | ContentType.XML
+ 'XML with parent node' | '/bookstore' | 'bookstore-categories-data.xml' | ContentType.XML
+ }
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy
index 18d0502e30..6c52becbe1 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy
@@ -26,7 +26,6 @@ import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.Anchor
import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder
import org.onap.cps.yang.YangTextSchemaSourceSet
-import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode
import org.opendaylight.yangtools.yang.model.api.SchemaContext
import spock.lang.Specification
@@ -47,6 +46,8 @@ class YangParserSpec extends Specification {
def containerNodeFromYangUtils = Mock(ContainerNode)
def noParent = ''
+ def validateOnly = true
+ def validateAndParse = false
def setup() {
mockYangTextSchemaSourceSetCache.get('my dataspace', 'my schema') >> mockYangTextSchemaSourceSet
@@ -55,7 +56,7 @@ class YangParserSpec extends Specification {
def 'Parsing data.'() {
given: 'the yang parser (utility) always returns a container node'
- mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent) >> containerNodeFromYangUtils
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateAndParse) >> containerNodeFromYangUtils
when: 'parsing some json data'
def result = objectUnderTest.parseData(ContentType.JSON, 'some json', anchor, noParent)
then: 'the schema source set for the correct dataspace and schema set is retrieved form the cache'
@@ -68,7 +69,7 @@ class YangParserSpec extends Specification {
def 'Parsing data with exception on first attempt.'() {
given: 'the yang parser throws an exception on the first attempt only'
- mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent) >> { throw new DataValidationException(noParent, noParent) } >> containerNodeFromYangUtils
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateAndParse) >> { throw new DataValidationException(noParent, noParent) } >> containerNodeFromYangUtils
when: 'attempt to parse some data'
def result = objectUnderTest.parseData(ContentType.JSON, 'some json', anchor, noParent)
then: 'the cache is cleared for the correct dataspace and schema'
@@ -79,7 +80,7 @@ class YangParserSpec extends Specification {
def 'Parsing data with exception on all attempts.'() {
given: 'the yang parser always throws an exception'
- mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent) >> { throw new DataValidationException(noParent, noParent) }
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateAndParse) >> { throw new DataValidationException(noParent, noParent) }
when: 'attempt to parse some data'
objectUnderTest.parseData(ContentType.JSON, 'some json', anchor, noParent)
then: 'a data validation exception is thrown'
@@ -94,9 +95,46 @@ class YangParserSpec extends Specification {
when: 'parsing some json data'
def result = objectUnderTest.parseData(ContentType.JSON, 'some json', yangResourcesNameToContentMap, noParent)
then: 'the yang parser helper always returns a container node'
- 1 * mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent) >> containerNodeFromYangUtils
+ 1 * mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateAndParse) >> containerNodeFromYangUtils
and: 'the result is the same container node as return from yang utils'
assert result == containerNodeFromYangUtils
}
+ def 'Validating #scenario data using Yang parser with cache retrieval.'() {
+ given: 'the yang parser (utility) is set up and schema context is available'
+ mockYangParserHelper.parseData(contentType, 'some json', mockSchemaContext, noParent, validateOnly)
+ when: 'attempt to parse data with no parent node xpath'
+ objectUnderTest.validateData(contentType, 'some json or xml data', anchor, noParent)
+ then: 'the correct schema set is retrieved from the cache for the dataspace and schema'
+ 1 * mockYangTextSchemaSourceSetCache.get('my dataspace', 'my schema') >> mockYangTextSchemaSourceSet
+ and: 'no cache entries are removed during validation'
+ 0 * mockYangTextSchemaSourceSetCache.removeFromCache(*_)
+ where:
+ scenario | contentType
+ 'JSON' | ContentType.JSON
+ 'XML' | ContentType.XML
+ }
+
+ def 'Validating data when parsing fails on first attempt and recovers.'() {
+ given: 'the Yang parser throws an exception on the first attempt but succeeds on the second'
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateOnly) >> { throw new DataValidationException(noParent, noParent) } >> null
+ when: 'attempting to parse JSON data'
+ objectUnderTest.validateData(ContentType.JSON, 'some json', anchor, noParent)
+ then: 'the cache is cleared for the correct dataspace and schema after the first failure'
+ 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my dataspace', 'my schema')
+ and: 'no exceptions are thrown after the second attempt'
+ noExceptionThrown()
+ }
+
+ def 'Validating data with repeated parsing failures leading to exception.'() {
+ given: 'the yang parser throws an exception on the first attempt only'
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateOnly) >> { throw new DataValidationException(noParent, noParent) }
+ when: 'attempting to parse JSON data'
+ objectUnderTest.validateData(ContentType.JSON, 'some json', anchor, noParent)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the cache is cleared for the correct dataspace and schema after the failure'
+ 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my dataspace', 'my schema')
+ }
+
}
diff --git a/cps-service/src/test/resources/application.yml b/cps-service/src/test/resources/application.yml
index b666885f22..be71d37d2a 100644
--- a/cps-service/src/test/resources/application.yml
+++ b/cps-service/src/test/resources/application.yml
@@ -1,6 +1,6 @@
# ============LICENSE_START=======================================================
# Copyright (c) 2021 Bell Canada.
-# Modification Copyright (C) 2022 Nordix Foundation.
+# Modification Copyright (C) 2022-2024 Nordix Foundation.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -41,11 +41,3 @@ spring:
logging:
level:
org.apache.kafka: ERROR
-
-# Custom Hazelcast Config.
-hazelcast:
- cluster-name: "cps-and-ncmp-test-caches"
- mode:
- kubernetes:
- enabled: false
- service-name: "cps-and-ncmp-service"
diff --git a/cps-service/src/test/resources/bookstore-categories-data.json b/cps-service/src/test/resources/bookstore-categories-data.json
new file mode 100644
index 0000000000..7dc22b17f7
--- /dev/null
+++ b/cps-service/src/test/resources/bookstore-categories-data.json
@@ -0,0 +1,49 @@
+{
+ "categories": [
+ {
+ "code": "01/1",
+ "name": "SciFi",
+ "books": [
+ {
+ "authors": [
+ "Iain M. Banks"
+ ],
+ "lang": "en/it",
+ "price": "895",
+ "pub_year": "1994",
+ "title": "Feersum Endjinn/Endjinn Feersum"
+ },
+ {
+ "authors": [
+ "Ursula K. Le Guin",
+ "Joe Haldeman",
+ "Orson Scott Card",
+ "david Brin",
+ "Rober Silverberg",
+ "Dan Simmons",
+ "Greg Bear"
+ ],
+ "lang": "en",
+ "price": "1099",
+ "pub_year": "1999",
+ "title": "Far Horizons"
+ }
+ ]
+ },
+ {
+ "name": "kids",
+ "code": "02",
+ "books": [
+ {
+ "authors": [
+ "Philip Pullman"
+ ],
+ "lang": "en",
+ "price": "699",
+ "pub_year": "1995",
+ "title": "The Golden Compass"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/cps-service/src/test/resources/bookstore-categories-data.xml b/cps-service/src/test/resources/bookstore-categories-data.xml
new file mode 100644
index 0000000000..c8592c1f90
--- /dev/null
+++ b/cps-service/src/test/resources/bookstore-categories-data.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<categories>
+ <code>1</code>
+ <name>SciFi</name>
+ <books>
+ <title>2001: A Space Odyssey</title>
+ <lang>en</lang>
+ <authors>
+ Iain M. Banks
+ </authors>
+ <pub_year>1994</pub_year>
+ <price>895</price>
+ </books>
+</categories> \ No newline at end of file
diff --git a/cps-service/src/test/resources/e2e/basic/cps-cavsta-onap-internal2021-01-28.yang b/cps-service/src/test/resources/e2e/basic/cps-cavsta-onap-internal2021-01-28.yang
index 32517398a3..2dabd79e8e 100644
--- a/cps-service/src/test/resources/e2e/basic/cps-cavsta-onap-internal2021-01-28.yang
+++ b/cps-service/src/test/resources/e2e/basic/cps-cavsta-onap-internal2021-01-28.yang
@@ -36,7 +36,7 @@ module cps-cavsta-onap-internal {
description
"RAN Network YANG Model for ONAP/O-RAN POC";
reference
- "https://wiki.onap.org/display/DW/E2E+Network+Slicing+Use+Case+in+R7+Guilin";
+ "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16414819/E2E+Network+Slicing+Use+Case+in+R7+Guilin";
}
typedef Tac {
diff --git a/cps-service/src/test/resources/e2e/basic/cps-ran-inventory@2021-01-28.yang b/cps-service/src/test/resources/e2e/basic/cps-ran-inventory@2021-01-28.yang
index c16a682512..2401409443 100644
--- a/cps-service/src/test/resources/e2e/basic/cps-ran-inventory@2021-01-28.yang
+++ b/cps-service/src/test/resources/e2e/basic/cps-ran-inventory@2021-01-28.yang
@@ -34,7 +34,7 @@ module cps-ran-inventory {
description
"RAN Network YANG Model for ONAP/O-RAN POC";
reference
- "https://wiki.onap.org/display/DW/E2E+Network+Slicing+Use+Case+in+R7+Guilin";
+ "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16414819/E2E+Network+Slicing+Use+Case+in+R7+Guilin";
}
typedef Mcc {
diff --git a/cps-service/src/test/resources/e2e/basic/cps-ran-schema-model@2021-05-19.yang b/cps-service/src/test/resources/e2e/basic/cps-ran-schema-model@2021-05-19.yang
index 5fd292a99d..3223b15e65 100644
--- a/cps-service/src/test/resources/e2e/basic/cps-ran-schema-model@2021-05-19.yang
+++ b/cps-service/src/test/resources/e2e/basic/cps-ran-schema-model@2021-05-19.yang
@@ -43,14 +43,14 @@ module cps-ran-schema-model {
description
"Added support for OOF PCI SON Use case";
reference
- "https://wiki.onap.org/display/DW/CPS+APIs";
+ "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16456851/CPS+APIs";
}
revision 2021-01-28 {
description
"CPS RAN Network YANG Model for ONAP/O-RAN POC";
reference
- "https://wiki.onap.org/display/DW/E2E+Network+Slicing+Use+Case+in+R7+Guilin";
+ "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16414819/E2E+Network+Slicing+Use+Case+in+R7+Guilin";
}
typedef usageState {
diff --git a/cps-service/src/test/resources/e2e/basic/ran-network2020-08-06.yang b/cps-service/src/test/resources/e2e/basic/ran-network2020-08-06.yang
index 5065659307..a4612e73fb 100755
--- a/cps-service/src/test/resources/e2e/basic/ran-network2020-08-06.yang
+++ b/cps-service/src/test/resources/e2e/basic/ran-network2020-08-06.yang
@@ -43,7 +43,7 @@ module ran-network {
description
"RAN Network YANG Model for ONAP/O-RAN POC";
reference
- "https://wiki.onap.org/display/DW/E2E+Network+Slicing+Use+Case+in+R7+Guilin";
+ "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16414819/E2E+Network+Slicing+Use+Case+in+R7+Guilin";
}
typedef usageState {
diff --git a/csit/install-deps.sh b/csit/install-deps.sh
new file mode 100755
index 0000000000..ef0b96a799
--- /dev/null
+++ b/csit/install-deps.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# Copyright 2024 Nordix Foundation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+echo "---> install-deps.sh"
+echo "Installing dependencies"
+
+# Create directory for downloaded binaries.
+mkdir -p bin
+touch bin/.gitignore
+
+# Add it to the PATH, so downloaded versions will be used.
+export PATH="$(pwd)/bin:$PATH"
+
+# Download docker-compose.
+if [ ! -x bin/docker-compose ]; then
+ echo " Downloading docker-compose"
+ curl -s -L https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64 > bin/docker-compose
+ chmod +x bin/docker-compose
+else
+ echo " docker-compose already installed"
+fi
+docker-compose version
diff --git a/csit/plans/cps/pnfsim/docker-compose.yml b/csit/plans/cps/pnfsim/docker-compose.yml
index e0f21a2ce6..869df22789 100644
--- a/csit/plans/cps/pnfsim/docker-compose.yml
+++ b/csit/plans/cps/pnfsim/docker-compose.yml
@@ -1,5 +1,5 @@
# ============LICENSE_START=======================================================
-# Modifications Copyright (C) 2022 Nordix Foundation
+# Modifications Copyright (C) 2022-2024 Nordix Foundation
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
services:
netconf-pnp-simulator:
- image: nexus3.onap.org:10001/onap/integration/simulators/netconf-pnp-simulator:2.8.6
+ image: blueonap/netconf-pnp-simulator:v2.8.6
container_name: netconf-simulator
restart: always
ports:
@@ -24,3 +24,4 @@ services:
- "6512:6513"
volumes:
- ./netconf-config:/config/modules/stores
+ - ./tls:/config/tls
diff --git a/csit/plans/cps/pnfsim/tls/ca.pem b/csit/plans/cps/pnfsim/tls/ca.pem
new file mode 100644
index 0000000000..4c4473815c
--- /dev/null
+++ b/csit/plans/cps/pnfsim/tls/ca.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID2zCCAsOgAwIBAgIUWDactJMMP2Q2mw0yBnUfQXRsXZMwDQYJKoZIhvcNAQEF
+BQAwfTELMAkGA1UEBhMCSUUxEjAQBgNVBAgMCVdlc3RtZWF0aDEQMA4GA1UEBwwH
+QXRobG9uZTEPMA0GA1UECgwGTm9yZGl4MRMwEQYDVQQDDApleGFtcGxlIENBMSIw
+IAYJKoZIhvcNAQkBFhNleGFtcGxlY2FAbG9jYWxob3N0MB4XDTI0MDcyNDExMzMw
+N1oXDTM0MDcyMjExMzMwN1owfTELMAkGA1UEBhMCSUUxEjAQBgNVBAgMCVdlc3Rt
+ZWF0aDEQMA4GA1UEBwwHQXRobG9uZTEPMA0GA1UECgwGTm9yZGl4MRMwEQYDVQQD
+DApleGFtcGxlIENBMSIwIAYJKoZIhvcNAQkBFhNleGFtcGxlY2FAbG9jYWxob3N0
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1mNXPz3Vx4l9zhKt7uBm
+8RFebZchO1WjAN5NiIVhVG9Vfktz3DVCbWYpZKwjRrf0g1vBbZk//6qCp6qhHB9m
+4KoDPR1Eu9SX9rri3TD1HWW05HRgxa5j/pk5PCt3/4+eZ31hKcJGsfJ1SwYDk3F/
+bUzgfZ5e4+2LDMgKmKtuhTzQP6ITmqpCN02nEKElDUXgTffo8QBwqnUN91vRmYnC
+9nfD68ipu2Nl19Jam0MRVue2kaZUXF4nisomY4Zmpcf45D6XAdUKMx5wr/kWULIc
+Dz2jE0BkOb/2GCT+sOMnI9riq2X3CoII2wn0NUw0oLYA6lKO5ICZ40w9LfCjeo/r
+yQIDAQABo1MwUTAdBgNVHQ4EFgQUa9fiOtMAq5lM20SZe3jHUnwaQHMwHwYDVR0j
+BBgwFoAUa9fiOtMAq5lM20SZe3jHUnwaQHMwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQUFAAOCAQEADys2rDXMYcjzhMhx0XJtty8STfBsWcsBfcVgwmt1vVwt
+buVn03vCVd90lj+5yqzr9OIntGEt/Mcw4Ca6rxl9bs+XGFxWo0McTxxXEZ5SRFK5
+ISRhWXWfmkxfiZalEymqKT4Xia8+Kydt0jsl93nUNA90GCQki7ngSCkOwoR4yizI
+eT6D/G5oTymEaKt8CuU+eBxQdD1kd6sSeKqXn4WY0dAClPk2VCjMuMYeYB6UWSL3
+HjSaDV4SQnCrvRNQzMJs/zONLPnt05N2GUho30LrXQ0h7zmkYl8AglfEtoCdXnRn
+ikOwkZ/N9V5K8NWJ0yQ5axftH6uxLMQgWIdhL32S0Q==
+-----END CERTIFICATE-----
diff --git a/csit/plans/cps/pnfsim/tls/server_cert.pem b/csit/plans/cps/pnfsim/tls/server_cert.pem
new file mode 100644
index 0000000000..a022dc56ca
--- /dev/null
+++ b/csit/plans/cps/pnfsim/tls/server_cert.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDijCCAnICFHAVskmbiSw4Q3eiKO6EJw48IS9EMA0GCSqGSIb3DQEBBQUAMH0x
+CzAJBgNVBAYTAklFMRIwEAYDVQQIDAlXZXN0bWVhdGgxEDAOBgNVBAcMB0F0aGxv
+bmUxDzANBgNVBAoMBk5vcmRpeDETMBEGA1UEAwwKZXhhbXBsZSBDQTEiMCAGCSqG
+SIb3DQEJARYTZXhhbXBsZWNhQGxvY2FsaG9zdDAeFw0yNDA3MjQxMTMzMzhaFw0z
+NDA3MjIxMTMzMzhaMIGFMQswCQYDVQQGEwJJRTESMBAGA1UECAwJV2VzdG1lYXRo
+MRAwDgYDVQQHDAdBdGhsb25lMQ8wDQYDVQQKDAZOb3JkaXgxFzAVBgNVBAMMDmV4
+YW1wbGUgc2VydmVyMSYwJAYJKoZIhvcNAQkBFhdleGFtcGxlc2VydmVyQGxvY2Fs
+aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALyFCBXEqZ39N7ZZ
+TEU8VJ03bY+kbTCfx9SOL/rP3X9zFOfv0g1TXEx2Yzl/LfRe1N5SgOB24tE34obA
+f++bOGXrsptrZMC5aqlG7cOfjELybUJaUIqMEDX+dte1f7OmPGs0mt2gG4DSU47j
+zGg3KshexLZUGc1fwPnUrBnEPFRCMWIqgSWkC4RrhB9R/uo/eBMh1coH+rSUE/Ba
+vcHlI8orbPu/mupt7tBKapb85nmSglatkZ/YCmfrrm4g5n8jap3e5rO8bs62yYeN
+BF2mHRLOwU+2VmQ1h6L+X0m6hC54UF9WdWyEd02o0HHDr1hDrg3aqrah+dnU+rgM
+hPu8ofkCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAzoHPz0msIst3sT0fQxjqYxYo
+TnU0XzFsMvGC08wbz/iNOS+nvcMuRgG06CUj53BJvdmZPhSfqiYcInM3F4m1MrbM
+dK6L6Vk2eWaL4GwV6B7FR0CWjtTdlETkLSMBNufiqgHebZCT88JDAZAeqhdEbsqk
+7bnZVDdD0qA1Z9ClXFU3jO6n8f5EFn9Ai7FhD7floLHb9M0lheE8xO60RPXNmq/r
+Vf8HrBGHqpiumsMyAuwJONliuSEXNGuB+J+XeQJG91O1oR4Of34HUEZBT/BkoM0X
+iFB+xrLbShsTh1RbAdd1+t76Lsc1lkDVoupaTpdTXA0EmouS9O3CAFWfTjlcGg==
+-----END CERTIFICATE-----
diff --git a/csit/plans/cps/pnfsim/tls/server_key.pem b/csit/plans/cps/pnfsim/tls/server_key.pem
new file mode 100644
index 0000000000..02fd68846d
--- /dev/null
+++ b/csit/plans/cps/pnfsim/tls/server_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8hQgVxKmd/Te2
+WUxFPFSdN22PpG0wn8fUji/6z91/cxTn79INU1xMdmM5fy30XtTeUoDgduLRN+KG
+wH/vmzhl67Kba2TAuWqpRu3Dn4xC8m1CWlCKjBA1/nbXtX+zpjxrNJrdoBuA0lOO
+48xoNyrIXsS2VBnNX8D51KwZxDxUQjFiKoElpAuEa4QfUf7qP3gTIdXKB/q0lBPw
+Wr3B5SPKK2z7v5rqbe7QSmqW/OZ5koJWrZGf2Apn665uIOZ/I2qd3uazvG7OtsmH
+jQRdph0SzsFPtlZkNYei/l9JuoQueFBfVnVshHdNqNBxw69YQ64N2qq2ofnZ1Pq4
+DIT7vKH5AgMBAAECggEAAJNHWwmmmtzS9rN/EBcHCxPIOc/+pU9XhMaKTvGjc2ge
+gDazJWdDuNgDpYFF2qEPdT47NnQmbQ0Gm/KqcUi/+0+k0+SYAh6OvMWCpD4wZ2Pm
+AXXVGRckVYXZRv8+zIWNWaZncpWyf8okhyMa1JaWgtYHM6c+DOpl5F1JySpYJMmt
+laWH0fMCYdYM5N8RisXfImmf+bBcehIZFvq47f5LefvPBHCss/L+Nym/ypMl+qo1
+MvVPkMNIhJFb/NQSYknp5ino2uo06RNOhftu/ig8CgzuSYvK18Ia9NEAKd2kS/y7
+MtYipgBCQqax5ulYmDAmnSrm+KpNhfk+CcMBlW9yoQKBgQDboMRpUMyinK9jrY7R
+GqISWcrDRFEwKfPr0rFtBZM/0ODjZBXMDSej28LwCo+6Vo8dF4G9fValuyyrNMQl
+T0OpcDxBKV3yCHZoXmzTKx/vcF56hOgkpwT8gHWpVsqVxcjIyERZrynblMnZixcv
+ppF33YJv8A7oParHGnj9zqWvVwKBgQDbvWlV7/CV5y5kYnNKLIZ51xaTZpAt1CE7
+N4B4x41y+jTGtscQoDlIMgOydC8F+dBeolvMXEyN002pYj8K9yxQcHCz2F62A2na
+ZA+Vj6xTq2/YGhBBrJ21eaEOcKBc9rrP0s2lzhzVb/fbPq0hkgWqQuJKsHaq2y9O
+fYUBfbB3LwKBgEexPgwmzPXT8ci28eS+LeORngeJuHrhZvc26qXs6Pku5Qo1NIxM
+SwFJDmQu/mXUNZlIgBhr3qnw5I7qhZCsRCj+Mx0ONNV5/7ToBdwUurL9WkniMqks
+QAtwn3fsleq4CmfIP8+Kxz4fXph9t87dL6USEK8bjLIw1xtxP8eR+jG3AoGAcuM4
+ZLcbqbSCW/fhYWGgOanMYurX7S4g5c4h/IQRH5FT8KV1tOqgqG+F4VK/lzdCy4fF
+yTZkzC4zR6FXZstOvwva0R0Kf82PFaEFSOQibGiRBIK0BzJSDqT2IQ+fuJtDlw8X
+eF4oUyvEgjvl10x6a8emeviCQthwhnA4D0yA6/8CgYEA14WLsOllb6IjO3c+mwFp
+Gs8pDrB/XPH7bPH1fVO+60OMT5OMTlEa/cVlhbNWuHVR7+yVQCh7HuzVPBtSdyNW
+4+8UuAz3eLm93he6DiH7D4U7Zx2TKB2B6PBbHz9aEh96l67TfAHy+u3x0mVFziZ7
+HNe49uMd7A5r4QgflshgDgs=
+-----END PRIVATE KEY-----
diff --git a/csit/plans/cps/sdnc/certs/keys0.zip b/csit/plans/cps/sdnc/certs/keys0.zip
index 48b4d90a10..b2dec5c7b2 100644
--- a/csit/plans/cps/sdnc/certs/keys0.zip
+++ b/csit/plans/cps/sdnc/certs/keys0.zip
Binary files differ
diff --git a/csit/plans/cps/setup.sh b/csit/plans/cps/setup.sh
index 80829eba14..036f14b82f 100755
--- a/csit/plans/cps/setup.sh
+++ b/csit/plans/cps/setup.sh
@@ -1,7 +1,6 @@
#!/bin/bash
#
# Copyright 2016-2017 Huawei Technologies Co., Ltd.
-# Modifications Copyright (C) 2022 Nordix Foundation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -19,7 +18,7 @@
# Modifications copyright (c) 2020-2021 Samsung Electronics Co., Ltd.
# Modifications Copyright (C) 2021 Pantheon.tech
# Modifications Copyright (C) 2021 Bell Canada.
-# Modifications Copyright (C) 2021 Nordix Foundation.
+# Modifications Copyright (C) 2021-2024 Nordix Foundation.
#
# Branched from ccsdk/distribution to this repository Feb 23, 2021
#
@@ -59,10 +58,6 @@ export $(cut -d= -f1 $WORKSPACE/plans/cps/test.properties)
###################### setup cps-ncmp ############################
cd $CPS_HOME/docker-compose
-curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` > docker-compose
-chmod +x docker-compose
-docker-compose version
-
# start CPS/NCMP, DMI Plugin, and PostgreSQL containers with docker compose
docker-compose --profile dmi-service up -d
@@ -82,4 +77,4 @@ check_health $DMI_HOST:$DMI_PORT 'dmi-plugin'
###################### ROBOT Configurations ##########################
# Pass variables required for Robot test suites in ROBOT_VARIABLES
-ROBOT_VARIABLES="-v CPS_CORE_HOST:$CPS_CORE_HOST -v CPS_CORE_PORT:$CPS_CORE_PORT -v DMI_HOST:$LOCAL_IP -v DMI_PORT:$DMI_PORT -v DMI_CSIT_STUB_HOST:$LOCAL_IP -v DMI_CSIT_STUB_PORT:$DMI_DEMO_STUB_PORT -v DMI_AUTH_ENABLED:$DMI_AUTH_ENABLED -v DATADIR_CPS_CORE:$WORKSPACE/data/cps-core -v DATADIR_NCMP:$WORKSPACE/data/ncmp -v DATADIR_SUBS_NOTIFICATION:$WORKSPACE/data/subscription-notification --exitonfailure" \ No newline at end of file
+ROBOT_VARIABLES="-v CPS_CORE_HOST:$CPS_CORE_HOST -v CPS_CORE_PORT:$CPS_CORE_PORT -v DMI_HOST:$LOCAL_IP -v DMI_PORT:$DMI_PORT -v DMI_CSIT_STUB_HOST:$LOCAL_IP -v DMI_CSIT_STUB_PORT:$DMI_DEMO_STUB_PORT -v DMI_AUTH_ENABLED:$DMI_AUTH_ENABLED -v DATADIR_CPS_CORE:$WORKSPACE/data/cps-core -v DATADIR_NCMP:$WORKSPACE/data/ncmp -v DATADIR_SUBS_NOTIFICATION:$WORKSPACE/data/subscription-notification --exitonfailure"
diff --git a/csit/plans/cps/teardown.sh b/csit/plans/cps/teardown.sh
index 7beb90722e..e2ecd885be 100755
--- a/csit/plans/cps/teardown.sh
+++ b/csit/plans/cps/teardown.sh
@@ -17,18 +17,24 @@
# Modifications copyright (c) 2017 AT&T Intellectual Property
# Modifications copyright (c) 2020 Samsung Electronics Co., Ltd.
# Modifications Copyright (C) 2021 Pantheon.tech
-# Modifications Copyright (C) 2021 Nordix Foundation
+# Modifications Copyright (C) 2021-2024 Nordix Foundation
# Branched from ccsdk/distribution to this repository Feb 23, 2021
#
echo '================================== docker info =========================='
docker ps -a
echo '================================== CPS-NCMP Logs ========================'
-docker logs cps-and-ncmp
+for CONTAINER_ID in $(docker ps --filter "name=cps-and-ncmp" --format "{{.ID}}"); do
+ echo "CPS-NCMP Logs for container: $CONTAINER_ID"
+ docker logs "$CONTAINER_ID"
+done
echo '================================== DMI Logs ============================='
docker logs ncmp-dmi-plugin
+echo '================================== DMI Stub Logs ========================'
+docker logs ncmp-dmi-plugin-demo-and-csit-stub
+
echo '================================== SDNC Logs ============================'
docker logs sdnc
diff --git a/csit/plans/cps/testplanNcmp.txt b/csit/plans/cps/testplanNcmp.txt
index e80f0d98a0..24c7745c71 100644
--- a/csit/plans/cps/testplanNcmp.txt
+++ b/csit/plans/cps/testplanNcmp.txt
@@ -21,4 +21,4 @@ cps-data-sync
ncmp-passthrough
cm-handle-query
cps-trust-level
-cps-data-operations \ No newline at end of file
+cps-data-operations
diff --git a/csit/prepare-csit.sh b/csit/prepare-csit.sh
index fbd5dc5f0d..1b8578e0ce 100755
--- a/csit/prepare-csit.sh
+++ b/csit/prepare-csit.sh
@@ -71,7 +71,7 @@ echo "Versioning information:"
python3 --version
echo "Installing confluent kafka library for robot framework:"
-pip install robotframework-confluentkafkalibrary
+pip install robotframework-confluentkafkalibrary==2.4.0-2
pip freeze
python3 -m robot.run --version || : \ No newline at end of file
diff --git a/csit/pylibs.txt b/csit/pylibs.txt
index 32bfa6faca..3eeb1ab9ec 100644
--- a/csit/pylibs.txt
+++ b/csit/pylibs.txt
@@ -9,7 +9,7 @@ robotframework-requests==0.9.3
robotframework-selenium2library==3.0.0
robotframework-extendedselenium2library
robotframework-sshlibrary
-robotframework-confluentkafkalibrary
+robotframework-confluentkafkalibrary==2.4.0-2
scapy
# Module jsonpath is needed by current AAA idmlite suite.
jsonpath-rw
diff --git a/csit/run-project-csit.sh b/csit/run-project-csit.sh
index fcb3c925c1..f362cc7130 100755
--- a/csit/run-project-csit.sh
+++ b/csit/run-project-csit.sh
@@ -2,6 +2,7 @@
#
# Copyright 2020-2021 © Samsung Electronics Co., Ltd.
# Modifications Copyright (C) 2021 Pantheon.tech
+# Modifications Copyright (C) 2024 Nordix Foundation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -28,6 +29,8 @@ rm -rf ${WORKSPACE}/archives
mkdir -p ${WORKSPACE}/archives
cd ${WORKSPACE}
+source install-deps.sh
+
# Execute all test-suites defined under plans subdirectory
for dir in plans/*/
do
diff --git a/csit/tests/cps-data-operations/cps-data-operations.robot b/csit/tests/cps-data-operations/cps-data-operations.robot
index 17dce16446..96212ff632 100644
--- a/csit/tests/cps-data-operations/cps-data-operations.robot
+++ b/csit/tests/cps-data-operations/cps-data-operations.robot
@@ -26,6 +26,7 @@ Library OperatingSystem
Library RequestsLibrary
Library BuiltIn
Library ConfluentKafkaLibrary
+Library String
Suite Setup Create Session CPS_URL http://${CPS_CORE_HOST}:${CPS_CORE_PORT}
@@ -48,7 +49,7 @@ NCMP Data Operation, forwarded to DMI, response on Client Topic
POST On Session CPS_URL ncmpInventory/v1/ch headers=${headers} data=${newCmHandleRequestBody}
${getCmHandleUri}= Set Variable ${ncmpBasePath}/v1/ch/CMHandle1
${getCmHandleHeaders}= Create Dictionary Authorization=${auth}
- Wait Until Keyword Succeeds 8sec 100ms Is CM Handle READY ${getCmHandleUri} ${getCmHandleHeaders} CMHandle1
+ Wait Until Keyword Succeeds 20sec 200ms Is CM Handle READY ${getCmHandleUri} ${getCmHandleHeaders} CMHandle1
${response}= POST On Session CPS_URL ${uri} params=${params} headers=${headers} data=${dataOperationReqBody}
Set Global Variable ${expectedRequestId} ${response.json()}[requestId]
Should Be Equal As Strings ${response.status_code} 200
@@ -63,7 +64,7 @@ Consume cloud event from client topic
Compare Header Values ${header_key_value_pair[0]} ${header_key_value_pair[1]} "ce_specversion" "1.0"
Compare Header Values ${header_key_value_pair[0]} ${header_key_value_pair[1]} "ce_type" "org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent"
Compare Header Values ${header_key_value_pair[0]} ${header_key_value_pair[1]} "ce_correlationid" "${expectedRequestId}"
- Compare Header Values ${header_key_value_pair[0]} ${header_key_value_pair[1]} "ce_source" "DMI"
+ Compare Header Values ${header_key_value_pair[0]} ${header_key_value_pair[1]} "ce_source" "DMI"
END
[Teardown] Basic Teardown ${group_id}
@@ -78,12 +79,20 @@ Is CM Handle READY
[Arguments] ${uri} ${headers} ${cmHandle}
${response}= GET On Session CPS_URL ${uri} headers=${headers}
Should Be Equal As Strings ${response.status_code} 200
+ ${number_of_items}= Count Items In JSON Response ${response}
+ Should Be True ${number_of_items} > 0
FOR ${item} IN ${response.json()}
IF "${item['cmHandle']}" == "${cmHandle}"
Should Be Equal As Strings ${item['state']['cmHandleState']} READY
END
END
+Count Items In JSON Response
+ [Arguments] ${response}
+ ${json_data}= Evaluate json.loads('${response.content.decode("utf-8")}') json
+ ${number_of_items}= Get Length ${json_data}
+ RETURN ${number_of_items}
+
Basic Teardown
[Arguments] ${group_id}
Unsubscribe ${group_id}
diff --git a/csit/tests/cps-data-sync/cps-data-sync.robot b/csit/tests/cps-data-sync/cps-data-sync.robot
index c0ee4da67b..b8ba479e7c 100644
--- a/csit/tests/cps-data-sync/cps-data-sync.robot
+++ b/csit/tests/cps-data-sync/cps-data-sync.robot
@@ -35,11 +35,6 @@ ${ncmpBasePath} /ncmp
*** Test Cases ***
-Check if ietfYang-PNFDemo is READY
- ${uri}= Set Variable ${ncmpBasePath}/v1/ch/ietfYang-PNFDemo
- ${headers}= Create Dictionary Authorization=${auth}
- Wait Until Keyword Succeeds 10sec 100ms Is CM Handle READY ${uri} ${headers} ietfYang-PNFDemo
-
Operational state goes to UNSYNCHRONIZED when data sync (flag) is enabled
${uri}= Set Variable ${ncmpBasePath}/v1/ch/ietfYang-PNFDemo/data-sync
${params}= Create Dictionary dataSyncEnabled=true
@@ -54,18 +49,9 @@ Operational state goes to UNSYNCHRONIZED when data sync (flag) is enabled
Operational state goes to SYNCHRONIZED after sometime when data sync (flag) is enabled
${uri}= Set Variable ${ncmpBasePath}/v1/ch/ietfYang-PNFDemo/state
${headers}= Create Dictionary Authorization=${auth}
- Wait Until Keyword Succeeds 10sec 100ms Is CM Handle State SYNCHRONIZED ${uri} ${headers}
+ Wait Until Keyword Succeeds 40sec 100ms Is CM Handle State SYNCHRONIZED ${uri} ${headers}
*** Keywords ***
-Is CM Handle READY
- [Arguments] ${uri} ${headers} ${cmHandle}
- ${response}= GET On Session CPS_URL ${uri} headers=${headers}
- Should Be Equal As Strings ${response.status_code} 200
- FOR ${item} IN ${response.json()}
- IF "${item['cmHandle']}" == "${cmHandle}"
- Should Be Equal As Strings ${item['state']['cmHandleState']} READY
- END
- END
Is CM Handle State SYNCHRONIZED
[Arguments] ${uri} ${headers}
diff --git a/csit/tests/cps-model-sync/cps-model-sync.robot b/csit/tests/cps-model-sync/cps-model-sync.robot
index bb881f6a67..514076f085 100644
--- a/csit/tests/cps-model-sync/cps-model-sync.robot
+++ b/csit/tests/cps-model-sync/cps-model-sync.robot
@@ -25,6 +25,7 @@ Library Collections
Library OperatingSystem
Library RequestsLibrary
Library BuiltIn
+Library String
Suite Setup Create Session CPS_URL http://${CPS_CORE_HOST}:${CPS_CORE_PORT}
@@ -88,13 +89,40 @@ Get cm handle details and confirm it has been deleted
${headers}= Create Dictionary Authorization=${auth}
${response}= GET On Session CPS_URL ${uri} headers=${headers} expected_status=404
+Check if ietfYang-PNFDemo is READY
+ ${uri}= Set Variable ${ncmpBasePath}/v1/ch/ietfYang-PNFDemo
+ ${headers}= Create Dictionary Authorization=${auth}
+ Wait Until Keyword Succeeds 20sec 200ms Is CM Handle READY ${uri} ${headers} ietfYang-PNFDemo
+
Get modules for registered data node
${uri}= Set Variable ${ncmpBasePath}/v1/ch/ietfYang-PNFDemo/modules
${headers}= Create Dictionary Authorization=${auth}
${response}= GET On Session CPS_URL ${uri} headers=${headers}
Should Be Equal As Strings ${response.status_code} 200
+ ${number_of_items}= Count Items In JSON Response ${response}
+ Should Be True ${number_of_items} > 0
FOR ${item} IN @{response.json()}
IF "${item['moduleName']}" == "stores"
Should Be Equal As Strings "${item['revision']}" "2020-09-15"
END
- END \ No newline at end of file
+ END
+
+*** Keywords ***
+
+Is CM Handle READY
+ [Arguments] ${uri} ${headers} ${cmHandle}
+ ${response}= GET On Session CPS_URL ${uri} headers=${headers}
+ Should Be Equal As Strings ${response.status_code} 200
+ ${number_of_items}= Count Items In JSON Response ${response}
+ Should Be True ${number_of_items} > 0
+ FOR ${item} IN ${response.json()}
+ IF "${item['cmHandle']}" == "${cmHandle}"
+ Should Be Equal As Strings ${item['state']['cmHandleState']} READY
+ END
+ END
+
+Count Items In JSON Response
+ [Arguments] ${response}
+ ${json_data}= Evaluate json.loads('${response.content.decode("utf-8")}') json
+ ${number_of_items}= Get Length ${json_data}
+ RETURN ${number_of_items} \ No newline at end of file
diff --git a/csit/tests/cps-trust-level/cps-trust-level.robot b/csit/tests/cps-trust-level/cps-trust-level.robot
index e4deeff32b..810bcf4d12 100644
--- a/csit/tests/cps-trust-level/cps-trust-level.robot
+++ b/csit/tests/cps-trust-level/cps-trust-level.robot
@@ -36,7 +36,7 @@ ${ncmpBasePath} /ncmp/v1
${dmiUrl} http://${DMI_HOST}:${DMI_PORT}
${jsonCreateCmHandles} {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":"","dmiModelPlugin":"","createdCmHandles":[{"trustLevel":"COMPLETE","cmHandle":"CH-1"},{"trustLevel":"COMPLETE","cmHandle":"CH-2"},{"cmHandle":"CH-3"},{"trustLevel":"NONE","cmHandle":"CH-4"}]}
${jsonTrustLevelPropertyQueryParameters} {"cmHandleQueryParameters": [{"conditionName": "cmHandleWithTrustLevel", "conditionParameters": [ {"trustLevel": "COMPLETE"} ] }]}
-${jsonTrustLevelQueryResponse} {"data":{"attributeValueChange":[{"attributeName":"trustLevel","newAttributeValue":"NONE"}]}}
+${jsonTrustLevelEventPayload} {"data":{"attributeValueChange":[{"attributeName":"trustLevel","newAttributeValue":"NONE"}]}}
*** Test Cases ***
Register data node
@@ -57,7 +57,7 @@ Verify notification
Compare Header Values ${header_key_value_pair[0]} ${header_key_value_pair[1]} "ce_type" "org.onap.cps.ncmp.events.avc.ncmp_to_client.AvcEvent"
Compare Header Values ${header_key_value_pair[0]} ${header_key_value_pair[1]} "ce_correlationid" "CH-4"
END
- Should Be Equal As Strings ${payload} ${jsonTrustLevelQueryResponse}
+ Should Be Equal As Strings ${payload} ${jsonTrustLevelEventPayload}
[Teardown] Basic Teardown ${group_id}
Retrieve CM Handle ids where query parameters Match (trust level query)
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml
deleted file mode 100644
index 3a678f6ddb..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml
+++ /dev/null
@@ -1,113 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ============LICENSE_START=======================================================
- Copyright (C) 2023-2024 Nordix Foundation
- ================================================================================
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- SPDX-License-Identifier: Apache-2.0
- ============LICENSE_END=========================================================
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.onap.cps</groupId>
- <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
- <version>3.5.1-SNAPSHOT</version>
- </parent>
-
- <artifactId>dmi-plugin-demo-and-csit-stub-app</artifactId>
-
- <properties>
- <app>org.onap.cps.ncmp.dmi.rest.stub.DmiDemoApplication</app>
- <maven.build.timestamp.format>yyyyMMdd'T'HHmmss'Z'</maven.build.timestamp.format>
- <base.image>${docker.pull.registry}/onap/integration-java17:12.0.0</base.image>
- <image.tag>${project.version}-${maven.build.timestamp}</image.tag>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- </properties>
-
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>com.google.cloud.tools</groupId>
- <artifactId>jib-maven-plugin</artifactId>
- <configuration>
- <container>
- <mainClass>${app}</mainClass>
- <creationTime>USE_CURRENT_TIMESTAMP</creationTime>
- </container>
- <from>
- <image>${base.image}</image>
- </from>
- <to>
- <tags>
- <tag>latest</tag>
- </tags>
- <image>${docker.push.registry}/onap/${image.name}:${image.tag}</image>
- </to>
- </configuration>
- <executions>
- <execution>
- <phase>package</phase>
- <id>build</id>
- <goals>
- <goal>dockerBuild</goal>
- </goals>
- </execution>
- <execution>
- <phase>deploy</phase>
- <id>buildAndPush</id>
- <goals>
- <goal>build</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </pluginManagement>
- </build>
-
- <profiles>
- <profile>
- <id>docker</id>
- <activation>
- <activeByDefault>true</activeByDefault>
- </activation>
- <properties>
- <image.name>dmi-plugin-demo-and-csit-stub</image.name>
- </properties>
- <build>
- <plugins>
- <plugin>
- <groupId>com.google.cloud.tools</groupId>
- <artifactId>jib-maven-plugin</artifactId>
- <version>3.3.2</version>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
- <dependencies>
- <dependency>
- <groupId>org.onap.cps</groupId>
- <artifactId>dmi-plugin-demo-and-csit-stub-service</artifactId>
- <version>${project.version}</version>
- <exclusions>
- <exclusion>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- </dependencies>
-</project> \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/DmiDemoApplication.java b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/DmiDemoApplication.java
deleted file mode 100644
index 2d4a2d8e82..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/DmiDemoApplication.java
+++ /dev/null
@@ -1,32 +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.ncmp.dmi.rest.stub;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-@SpringBootApplication
-public class DmiDemoApplication {
-
- public static void main(final String[] args) {
- SpringApplication.run(DmiDemoApplication.class, args);
- }
-}
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml
deleted file mode 100644
index 73469de596..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<!--
- ============LICENSE_START=======================================================
- Copyright (C) 2023-2024 Nordix Foundation
- ================================================================================
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- SPDX-License-Identifier: Apache-2.0
- ============LICENSE_END=========================================================
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.onap.cps</groupId>
- <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
- <version>3.5.1-SNAPSHOT</version>
- </parent>
- <artifactId>dmi-plugin-demo-and-csit-stub-service</artifactId>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-tomcat</artifactId>
- </exclusion>
- </exclusions>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-actuator</artifactId>
- </dependency>
- <dependency>
- <groupId>com.googlecode.json-simple</groupId>
- <artifactId>json-simple</artifactId>
- <version>1.1.1</version>
- <exclusions>
- <exclusion>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <dependency>
- <groupId>org.onap.cps</groupId>
- <artifactId>cps-ncmp-rest</artifactId>
- </dependency>
- </dependencies>
-</project>
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java
deleted file mode 100644
index 3891d5b72b..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2023-2024 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.dmi.rest.stub.controller;
-
-import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESS;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.cloudevents.CloudEvent;
-import io.cloudevents.core.builder.CloudEventBuilder;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.json.simple.parser.JSONParser;
-import org.json.simple.parser.ParseException;
-import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DataOperationRequest;
-import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiDataOperationRequest;
-import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiOperationCmHandle;
-import org.onap.cps.ncmp.dmi.rest.stub.utils.ResourceFileReaderUtil;
-import org.onap.cps.ncmp.events.async1_0_0.Data;
-import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent;
-import org.onap.cps.ncmp.events.async1_0_0.Response;
-import org.onap.cps.ncmp.impl.utils.EventDateTimeFormatter;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationContext;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.kafka.core.KafkaTemplate;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestHeader;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-@RequestMapping("${rest.api.dmi-stub-base-path}")
-@RequiredArgsConstructor
-@Slf4j
-public class DmiRestStubController {
-
- private static final String DEFAULT_TAG = "tagD";
- private static final String dataOperationEventType = "org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent";
- private static final Map<String, String> moduleSetTagPerCmHandleId = new HashMap<>();
- private final KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate;
- private final ObjectMapper objectMapper;
- private final ApplicationContext applicationContext;
- @Value("${app.ncmp.async-m2m.topic}")
- private String ncmpAsyncM2mTopic;
- @Value("${delay.module-references-delay-ms}")
- private long moduleReferencesDelayMs;
- @Value("${delay.module-resources-delay-ms}")
- private long moduleResourcesDelayMs;
- @Value("${delay.data-for-cm-handle-delay-ms}")
- private long dataForCmHandleDelayMs;
-
- /**
- * This code defines a REST API endpoint for adding new the module set tag mapping. The endpoint receives the
- * cmHandleId and moduleSetTag as request body and add into moduleSetTagPerCmHandleId map with the provided
- * values.
- *
- * @param requestBody map of cmHandleId and moduleSetTag
- * @return a ResponseEntity object containing the updated moduleSetTagPerCmHandleId map as the response body
- */
- @PostMapping("/v1/tagMapping")
- public ResponseEntity<Map<String, String>> addTagForMapping(@RequestBody final Map<String, String> requestBody) {
- moduleSetTagPerCmHandleId.putAll(requestBody);
- return new ResponseEntity<>(requestBody, HttpStatus.CREATED);
- }
-
- /**
- * This code defines a GET endpoint of module set tag mapping.
- *
- * @return The map represents the module set tag mapping.
- */
- @GetMapping("/v1/tagMapping")
- public ResponseEntity<Map<String, String>> getTagMapping() {
- return ResponseEntity.ok(moduleSetTagPerCmHandleId);
- }
-
- /**
- * This code defines a GET endpoint of module set tag by cm handle ID.
- *
- * @return The map represents the module set tag mapping filtered by cm handle ID.
- */
- @GetMapping("/v1/tagMapping/ch/{cmHandleId}")
- public ResponseEntity<String> getTagMappingByCmHandleId(@PathVariable final String cmHandleId) {
- return ResponseEntity.ok(moduleSetTagPerCmHandleId.get(cmHandleId));
- }
-
- /**
- * This code defines a REST API endpoint for updating the module set tag mapping. The endpoint receives the
- * cmHandleId and moduleSetTag as request body and updates the moduleSetTagPerCmHandleId map with the provided
- * values.
- *
- * @param requestBody map of cmHandleId and moduleSetTag
- * @return a ResponseEntity object containing the updated moduleSetTagPerCmHandleId map as the response body
- */
-
- @PutMapping("/v1/tagMapping")
- public ResponseEntity<Map<String, String>> updateTagMapping(@RequestBody final Map<String, String> requestBody) {
- moduleSetTagPerCmHandleId.putAll(requestBody);
- return ResponseEntity.noContent().build();
- }
-
- /**
- * It contains a method to delete an entry from the moduleSetTagPerCmHandleId map.
- * The method takes a cmHandleId as a parameter and removes the corresponding entry from the map.
- *
- * @return a ResponseEntity containing the updated map.
- */
- @DeleteMapping("/v1/tagMapping/ch/{cmHandleId}")
- public ResponseEntity<String> deleteTagMappingByCmHandleId(@PathVariable final String cmHandleId) {
- moduleSetTagPerCmHandleId.remove(cmHandleId);
- return ResponseEntity.ok(String.format("Mapping of %s is deleted successfully", cmHandleId));
- }
-
- /**
- * Get all modules for given cm handle.
- *
- * @param cmHandleId The identifier for a network function, network element, subnetwork,
- * or any other cm object by managed Network CM Proxy
- * @param moduleReferencesRequest module references request body
- * @return ResponseEntity response entity having module response as json string.
- */
- @PostMapping("/v1/ch/{cmHandleId}/modules")
- public ResponseEntity<String> getModuleReferences(@PathVariable("cmHandleId") final String cmHandleId,
- @RequestBody final Object moduleReferencesRequest) {
- delay(moduleReferencesDelayMs);
- try {
- log.info("Incoming DMI request body: {}",
- objectMapper.writeValueAsString(moduleReferencesRequest));
- } catch (final JsonProcessingException jsonProcessingException) {
- log.info("Unable to parse dmi data operation request to json string");
- }
- final String moduleResponseContent = getModuleResourceResponse(cmHandleId,
- "ModuleResponse.json");
- log.info("cm handle: {} requested for modules", cmHandleId);
- return ResponseEntity.ok(moduleResponseContent);
- }
-
- /**
- * Retrieves module resources for a given cmHandleId.
- *
- * @param cmHandleId The identifier for a network function, network element, subnetwork,
- * or any other cm object by managed Network CM Proxy
- * @param moduleResourcesReadRequest module resources read request body
- * @return ResponseEntity response entity having module resources response as json string.
- */
- @PostMapping("/v1/ch/{cmHandleId}/moduleResources")
- public ResponseEntity<String> retrieveModuleResources(
- @PathVariable("cmHandleId") final String cmHandleId,
- @RequestBody final Object moduleResourcesReadRequest) {
- delay(moduleResourcesDelayMs);
- final String moduleResourcesResponseContent = getModuleResourceResponse(cmHandleId,
- "ModuleResourcesResponse.json");
- log.info("cm handle: {} requested for modules resources", cmHandleId);
- return ResponseEntity.ok(moduleResourcesResponseContent);
- }
-
- /**
- * Create resource data from passthrough operational or running for a cm handle.
- *
- * @param cmHandleId The identifier for a network function, network element, subnetwork,
- * or any other cm object by managed Network CM Proxy
- * @param datastoreName datastore name
- * @param resourceIdentifier resource identifier
- * @param options options
- * @param topic client given topic name
- * @return (@ code ResponseEntity) response entity
- */
- @PostMapping("/v1/ch/{cmHandleId}/data/ds/{datastoreName}")
- public ResponseEntity<String> getResourceDataForCmHandle(
- @PathVariable("cmHandleId") final String cmHandleId,
- @PathVariable("datastoreName") final String datastoreName,
- @RequestParam(value = "resourceIdentifier") final String resourceIdentifier,
- @RequestParam(value = "options", required = false) final String options,
- @RequestParam(value = "topic", required = false) final String topic,
- @RequestHeader(value = "Authorization", required = false) final String authorization,
- @RequestBody final String requestBody) {
- log.info("DMI AUTH HEADER: {}", authorization);
- delay(dataForCmHandleDelayMs);
- log.info("Logging request body {}", requestBody);
-
- final String sampleJson = ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
- ResourceLoader.CLASSPATH_URL_PREFIX + "data/operational/ietf-network-topology-sample-rfc8345.json"));
- return ResponseEntity.ok(sampleJson);
- }
-
- /**
- * This method is not implemented for ONAP DMI plugin.
- *
- * @param topic client given topic name
- * @param requestId requestId generated by NCMP as an ack for client
- * @param dmiDataOperationRequest list of operation details
- * @return (@ code ResponseEntity) response entity
- */
- @PostMapping("/v1/data")
- public ResponseEntity<Void> getResourceDataForCmHandleDataOperation(
- @RequestParam(value = "topic") final String topic,
- @RequestParam(value = "requestId") final String requestId,
- @RequestBody final DmiDataOperationRequest dmiDataOperationRequest) {
- delay(dataForCmHandleDelayMs);
- try {
- log.info("Request received from the NCMP to DMI Plugin: {}",
- objectMapper.writeValueAsString(dmiDataOperationRequest));
- } catch (final JsonProcessingException jsonProcessingException) {
- log.info("Unable to process dmi data operation request to json string");
- }
- dmiDataOperationRequest.getOperations().forEach(dmiDataOperation -> {
- final DataOperationEvent dataOperationEvent = getDataOperationEvent(dmiDataOperation);
- dmiDataOperation.getCmHandles().forEach(dmiOperationCmHandle -> {
- log.info("Module Set Tag received: {}", dmiOperationCmHandle.getModuleSetTag());
- dataOperationEvent.getData().getResponses().get(0).setIds(List.of(dmiOperationCmHandle.getId()));
- final CloudEvent cloudEvent = buildAndGetCloudEvent(topic, requestId, dataOperationEvent);
- cloudEventKafkaTemplate.send(ncmpAsyncM2mTopic, UUID.randomUUID().toString(), cloudEvent);
- });
- });
- return new ResponseEntity<>(HttpStatus.ACCEPTED);
- }
-
- private CloudEvent buildAndGetCloudEvent(final String topic, final String requestId,
- final DataOperationEvent dataOperationEvent) {
- CloudEvent cloudEvent = null;
- try {
- cloudEvent = CloudEventBuilder.v1()
- .withId(UUID.randomUUID().toString())
- .withSource(URI.create("DMI"))
- .withType(dataOperationEventType)
- .withDataSchema(URI.create("urn:cps:" + dataOperationEventType + ":1.0.0"))
- .withTime(EventDateTimeFormatter.toIsoOffsetDateTime(
- EventDateTimeFormatter.getCurrentIsoFormattedDateTime()))
- .withData(objectMapper.writeValueAsBytes(dataOperationEvent))
- .withExtension("destination", topic)
- .withExtension("correlationid", requestId)
- .build();
- } catch (final JsonProcessingException jsonProcessingException) {
- log.error("Unable to parse event into bytes. cause : {}", jsonProcessingException.getMessage());
- }
- return cloudEvent;
- }
-
- private DataOperationEvent getDataOperationEvent(final DataOperationRequest dataOperationRequest) {
- final Response response = new Response();
-
- response.setOperationId(dataOperationRequest.getOperationId());
- response.setStatusCode(SUCCESS.getCode());
- response.setStatusMessage(SUCCESS.getMessage());
- response.setIds(dataOperationRequest.getCmHandles().stream().map(DmiOperationCmHandle::getId).toList());
- response.setResourceIdentifier(dataOperationRequest.getResourceIdentifier());
- response.setOptions(dataOperationRequest.getOptions());
- final String ietfNetworkTopologySample = ResourceFileReaderUtil
- .getResourceFileContent(applicationContext.getResource(
- ResourceLoader.CLASSPATH_URL_PREFIX
- + "data/operational/ietf-network-topology-sample-rfc8345.json"));
- final JSONParser jsonParser = new JSONParser();
- try {
- response.setResult(jsonParser.parse(ietfNetworkTopologySample));
- } catch (final ParseException parseException) {
- log.error("Unable to parse event result as json object. cause : {}", parseException.getMessage());
- }
- final List<Response> responseList = new ArrayList<>(1);
- responseList.add(response);
- final Data data = new Data();
- data.setResponses(responseList);
- final DataOperationEvent dataOperationEvent = new DataOperationEvent();
- dataOperationEvent.setData(data);
- return dataOperationEvent;
- }
-
- private String getModuleResourceResponse(final String cmHandleId, final String moduleResponseType) {
- if (moduleSetTagPerCmHandleId.isEmpty()) {
- log.info("Using default module responses of type ietfYang");
- return ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
- ResourceLoader.CLASSPATH_URL_PREFIX
- + String.format("module/ietfYang-%s", moduleResponseType)));
- }
- final String moduleSetTag = moduleSetTagPerCmHandleId.getOrDefault(cmHandleId, DEFAULT_TAG);
- final String moduleResponseFilePath = String.format("module/%s-%s", moduleSetTag, moduleResponseType);
- final Resource moduleResponseResource = applicationContext.getResource(
- ResourceLoader.CLASSPATH_URL_PREFIX + moduleResponseFilePath);
- log.info("Using module responses from : {}", moduleResponseFilePath);
- return ResourceFileReaderUtil.getResourceFileContent(moduleResponseResource);
- }
-
- private void delay(final long milliseconds) {
- try {
- Thread.sleep(milliseconds);
- } catch (final InterruptedException e) {
- log.error("Thread sleep interrupted: {}", e.getMessage());
- Thread.currentThread().interrupt();
- }
- }
-}
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DataOperationRequest.java b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DataOperationRequest.java
deleted file mode 100644
index 410774932b..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DataOperationRequest.java
+++ /dev/null
@@ -1,39 +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.ncmp.dmi.rest.stub.model.data.operational;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import java.util.ArrayList;
-import java.util.List;
-import lombok.Getter;
-import lombok.Setter;
-
-@JsonInclude(JsonInclude.Include.NON_NULL)
-@Setter
-@Getter
-public class DataOperationRequest {
- private String operation;
- private String operationId;
- private String datastore;
- private String options;
- private String resourceIdentifier;
- private List<DmiOperationCmHandle> cmHandles = new ArrayList<>();
-}
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiDataOperationRequest.java b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiDataOperationRequest.java
deleted file mode 100644
index ae78c0337a..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiDataOperationRequest.java
+++ /dev/null
@@ -1,33 +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.ncmp.dmi.rest.stub.model.data.operational;
-
-import java.util.List;
-import lombok.Getter;
-import lombok.Setter;
-
-@Setter
-@Getter
-public class DmiDataOperationRequest {
-
- private List<DataOperationRequest> operations;
-
-}
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ResourceFileReaderUtil.java b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ResourceFileReaderUtil.java
deleted file mode 100644
index 0d2adee43e..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ResourceFileReaderUtil.java
+++ /dev/null
@@ -1,51 +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.ncmp.dmi.rest.stub.utils;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.core.io.Resource;
-import org.springframework.util.StreamUtils;
-
-/**
- * Common convenience methods for reading resource file content.
- */
-@Slf4j
-public class ResourceFileReaderUtil {
-
- /**
- * Converts a resource file content into string.
- *
- * @param fileClasspath to name of the file in test/resources
- * @return the content of the file as a String
- * @throws IOException when there is an IO issue
- */
- public static String getResourceFileContent(final Resource fileClasspath) {
- String fileContent = null;
- try {
- fileContent = StreamUtils.copyToString(fileClasspath.getInputStream(), StandardCharsets.UTF_8);
- } catch (final IOException ioException) {
- log.debug("unable to read resource file content. cause : {}", ioException.getMessage());
- }
- return fileContent;
- }
-}
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/application.yml b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/application.yml
deleted file mode 100644
index b78a5b2db5..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/application.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-# ============LICENSE_START=======================================================
-# Copyright (C) 2023-2024 Nordix Foundation
-# ================================================================================
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# SPDX-License-Identifier: Apache-2.0
-# ============LICENSE_END=========================================================
-server:
- port: 8092
- jetty:
- threads:
- max: 25
-
-rest:
- api:
- dmi-stub-base-path: /dmi
-
-spring:
- main:
- banner-mode: "off"
- application:
- name: "dmi-plugin-demo-and-csit-stub"
-
- kafka:
- bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVER:localhost:19092}
- security:
- protocol: PLAINTEXT
- producer:
- value-serializer: io.cloudevents.kafka.CloudEventSerializer
- client-id: cps-core
-
-management:
- endpoints:
- web:
- exposure:
- include: health
-
-app:
- ncmp:
- async-m2m:
- topic: ${NCMP_ASYNC_M2M_TOPIC:ncmp-async-m2m}
-
-delay:
- module-references-delay-ms: ${MODULE_REFERENCES_DELAY_MS:100}
- module-resources-delay-ms: ${MODULE_RESOURCES_DELAY_MS:1000}
- data-for-cm-handle-delay-ms: ${DATA_FOR_CM_HANDLE_DELAY_MS:2500}
-
-logging:
- level:
- org:
- springframework:
- web:
- filter:
- CommonsRequestLoggingFilter: DEBUG \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/data/operational/ietf-network-topology-sample-rfc8345.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/data/operational/ietf-network-topology-sample-rfc8345.json
deleted file mode 100644
index 8f9dbc2259..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/data/operational/ietf-network-topology-sample-rfc8345.json
+++ /dev/null
@@ -1,76 +0,0 @@
-{
- "ietf-network:networks": {
- "network": [
- {
- "network-types": {
- },
- "network-id": "otn-hc",
- "node": [
- {
- "node-id": "D1",
- "termination-point": [
- {
- "tp-id": "1-0-1"
- },
- {
- "tp-id": "1-2-1"
- },
- {
- "tp-id": "1-3-1"
- }
- ]
- },
- {
- "node-id": "D2",
- "termination-point": [
- {
- "tp-id": "2-0-1"
- },
- {
- "tp-id": "2-1-1"
- },
- {
- "tp-id": "2-3-1"
- }
- ]
- },
- {
- "node-id": "D3",
- "termination-point": [
- {
- "tp-id": "3-1-1"
- },
- {
- "tp-id": "3-2-1"
- }
- ]
- }
- ],
- "ietf-network-topology:link": [
- {
- "link-id": "D1,1-2-1,D2,2-1-1",
- "source": {
- "source-node": "D1",
- "source-tp": "1-2-1"
- },
- "destination": {
- "dest-node": "D2",
- "dest-tp": "2-1-1"
- }
- },
- {
- "link-id": "D2,2-1-1,D1,1-2-1",
- "source": {
- "source-node": "D2",
- "source-tp": "2-1-1"
- },
- "destination": {
- "dest-node": "D1",
- "dest-tp": "1-2-1"
- }
- }
- ]
- }
- ]
- }
- }
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/ietfYang-ModuleResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/ietfYang-ModuleResourcesResponse.json
deleted file mode 100644
index 4326733f5e..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/ietfYang-ModuleResourcesResponse.json
+++ /dev/null
@@ -1,52 +0,0 @@
-[
- {
- "moduleName": "ietf-yang-types-1",
- "revision": "2013-07-15",
- "yangSource": "module ietf-yang-types-1 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-1\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "ietf-yang-types-2",
- "revision": "2013-07-16",
- "yangSource": "module ietf-yang-types-2 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-2\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-16 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "ietf-yang-types-3",
- "revision": "2013-07-17",
- "yangSource": "module ietf-yang-types-3 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-3\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-17 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "ietf-yang-types-4",
- "revision": "2013-07-18",
- "yangSource": "module ietf-yang-types-4 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-4\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-18 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "ietf-yang-types-5",
- "revision": "2013-07-19",
- "yangSource": "module ietf-yang-types-5 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-5\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-19 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "ietf-yang-types-6",
- "revision": "2013-07-20",
- "yangSource": "module ietf-yang-types-6 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-6\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-20 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "ietf-yang-types-7",
- "revision": "2013-07-21",
- "yangSource": "module ietf-yang-types-7 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-7\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-21 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "ietf-yang-types-8",
- "revision": "2013-07-22",
- "yangSource": "module ietf-yang-types-8 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-8\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-22 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "ietf-yang-types-9",
- "revision": "2013-07-23",
- "yangSource": "module ietf-yang-types-9 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-9\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-23 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "ietf-yang-types-10",
- "revision": "2013-07-24",
- "yangSource": "module ietf-yang-types-10 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-10\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-24 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- }
-] \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/ietfYang-ModuleResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/ietfYang-ModuleResponse.json
deleted file mode 100644
index 2cbd8d1313..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/ietfYang-ModuleResponse.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "schemas": [
- {
- "moduleName": "ietf-yang-types-1",
- "revision": "2013-07-15"
- },
- {
- "moduleName": "ietf-yang-types-2",
- "revision": "2013-07-16"
- },
- {
- "moduleName": "ietf-yang-types-3",
- "revision": "2013-07-17"
- },
- {
- "moduleName": "ietf-yang-types-4",
- "revision": "2013-07-18"
- },
- {
- "moduleName": "ietf-yang-types-5",
- "revision": "2013-07-19"
- },
- {
- "moduleName": "ietf-yang-types-6",
- "revision": "2013-07-20"
- },
- {
- "moduleName": "ietf-yang-types-7",
- "revision": "2013-07-21"
- },
- {
- "moduleName": "ietf-yang-types-8",
- "revision": "2013-07-22"
- },
- {
- "moduleName": "ietf-yang-types-9",
- "revision": "2013-07-23"
- },
- {
- "moduleName": "ietf-yang-types-10",
- "revision": "2013-07-24"
- }
- ]
-} \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagA-ModuleResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagA-ModuleResourcesResponse.json
deleted file mode 100644
index 5d713914d6..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagA-ModuleResourcesResponse.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
- {
- "moduleName": "M1",
- "revision": "2024-01-01",
- "yangSource": "module M1 {\n\n namespace \"urn:ietf:params:xml:ns:yang:M1\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2024-01-01 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "M2",
- "revision": "2024-01-02",
- "yangSource": "module M2 {\n\n namespace \"urn:ietf:params:xml:ns:yang:M2\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2024-01-02 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- }
-] \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagA-ModuleResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagA-ModuleResponse.json
deleted file mode 100644
index 9f20564f04..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagA-ModuleResponse.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "schemas": [
- {
- "moduleName": "M1",
- "revision": "2024-01-01"
- },
- {
- "moduleName": "M2",
- "revision": "2024-01-02"
- }
- ]
-} \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagB-ModuleResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagB-ModuleResourcesResponse.json
deleted file mode 100644
index ef9b85f926..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagB-ModuleResourcesResponse.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
- {
- "moduleName": "M1",
- "revision": "2024-01-01",
- "yangSource": "module M1 {\n\n namespace \"urn:ietf:params:xml:ns:yang:M1\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2024-01-01 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "M3",
- "revision": "2024-01-03",
- "yangSource": "module M3 {\n\n namespace \"urn:ietf:params:xml:ns:yang:M3\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2024-01-03 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- }
-] \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagB-ModuleResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagB-ModuleResponse.json
deleted file mode 100644
index 513c749a26..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagB-ModuleResponse.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "schemas": [
- {
- "moduleName": "M1",
- "revision": "2024-01-01"
- },
- {
- "moduleName": "M3",
- "revision": "2024-01-03"
- }
- ]
-} \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagC-ModuleResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagC-ModuleResourcesResponse.json
deleted file mode 100644
index 8fb696bb7b..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagC-ModuleResourcesResponse.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
- {
- "moduleName": "M4",
- "revision": "2024-01-04",
- "yangSource": "module M4 {\n\n namespace \"urn:ietf:params:xml:ns:yang:M1\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2024-01-04 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "M5",
- "revision": "2024-01-05",
- "yangSource": "module M5 {\n\n namespace \"urn:ietf:params:xml:ns:yang:M2\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2024-01-05 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- }
-] \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagC-ModuleResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagC-ModuleResponse.json
deleted file mode 100644
index ea22d8b6dd..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagC-ModuleResponse.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "schemas": [
- {
- "moduleName": "M4",
- "revision": "2024-01-04"
- },
- {
- "moduleName": "M5",
- "revision": "2024-01-05"
- }
- ]
-} \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagD-ModuleResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagD-ModuleResourcesResponse.json
deleted file mode 100644
index 5d713914d6..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagD-ModuleResourcesResponse.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
- {
- "moduleName": "M1",
- "revision": "2024-01-01",
- "yangSource": "module M1 {\n\n namespace \"urn:ietf:params:xml:ns:yang:M1\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2024-01-01 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- },
- {
- "moduleName": "M2",
- "revision": "2024-01-02",
- "yangSource": "module M2 {\n\n namespace \"urn:ietf:params:xml:ns:yang:M2\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2024-01-02 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
- }
-] \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagD-ModuleResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagD-ModuleResponse.json
deleted file mode 100644
index 9f20564f04..0000000000
--- a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/tagD-ModuleResponse.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "schemas": [
- {
- "moduleName": "M1",
- "revision": "2024-01-01"
- },
- {
- "moduleName": "M2",
- "revision": "2024-01-02"
- }
- ]
-} \ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/pom.xml b/dmi-plugin-demo-and-csit-stub/pom.xml
deleted file mode 100644
index a61c985792..0000000000
--- a/dmi-plugin-demo-and-csit-stub/pom.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ============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=========================================================
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.onap.cps</groupId>
- <artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
- <relativePath>../cps-parent/pom.xml</relativePath>
- </parent>
-
- <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
- <packaging>pom</packaging>
-
- <properties>
- <parent.directory>${project.parent.basedir}/..</parent.directory>
- <sonar.skip>true</sonar.skip>
- <jacoco.skip>true</jacoco.skip>
- </properties>
-
- <modules>
- <module>dmi-plugin-demo-and-csit-stub-service</module>
- <module>dmi-plugin-demo-and-csit-stub-app</module>
- </modules>
-</project> \ No newline at end of file
diff --git a/docker-compose/config/grafana/jvm-micrometer-dashboard.json b/docker-compose/config/grafana/jvm-micrometer-dashboard.json
new file mode 100644
index 0000000000..8f7747c596
--- /dev/null
+++ b/docker-compose/config/grafana/jvm-micrometer-dashboard.json
@@ -0,0 +1,3613 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "limit": 100,
+ "name": "Annotations & Alerts",
+ "showIn": 0,
+ "type": "dashboard"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "enable": true,
+ "expr": "resets(process_uptime_seconds{application=\"$application\", instance=\"$instance\"}[1m]) > 0",
+ "iconColor": "rgba(255, 96, 96, 1)",
+ "name": "Restart Detection",
+ "showIn": 0,
+ "step": "1m",
+ "tagKeys": "restart-tag",
+ "textFormat": "uptime reset",
+ "titleFormat": "Restart"
+ }
+ ]
+ },
+ "description": "Dashboard for Micrometer instrumented applications (Java, Spring Boot, Micronaut)",
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "gnetId": 4701,
+ "graphTooltip": 1,
+ "id": 1,
+ "links": [],
+ "panels": [
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 139,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Quick Facts",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "decimals": 1,
+ "mappings": [
+ {
+ "options": {
+ "match": "null",
+ "result": {
+ "text": "N/A"
+ }
+ },
+ "type": "special"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 6,
+ "x": 0,
+ "y": 1
+ },
+ "id": 63,
+ "maxDataPoints": 100,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "11.1.4",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_uptime_seconds{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "",
+ "metric": "",
+ "refId": "A",
+ "step": 14400
+ }
+ ],
+ "title": "Uptime",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [
+ {
+ "options": {
+ "match": "null",
+ "result": {
+ "text": "N/A"
+ }
+ },
+ "type": "special"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "dateTimeAsIso"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 6,
+ "x": 6,
+ "y": 1
+ },
+ "id": 92,
+ "maxDataPoints": 100,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "11.1.4",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_start_time_seconds{application=\"$application\", instance=\"$instance\"}*1000",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "",
+ "metric": "",
+ "refId": "A",
+ "step": 14400
+ }
+ ],
+ "title": "Start time",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "decimals": 2,
+ "mappings": [
+ {
+ "options": {
+ "match": "null",
+ "result": {
+ "text": "N/A"
+ }
+ },
+ "type": "special"
+ }
+ ],
+ "max": 100,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "rgba(50, 172, 45, 0.97)",
+ "value": null
+ },
+ {
+ "color": "rgba(237, 129, 40, 0.89)",
+ "value": 70
+ },
+ {
+ "color": "rgba(245, 54, 54, 0.9)",
+ "value": 90
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 6,
+ "x": 12,
+ "y": 1
+ },
+ "id": 65,
+ "maxDataPoints": 100,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "11.1.4",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"heap\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "",
+ "refId": "A",
+ "step": 14400
+ }
+ ],
+ "title": "Heap used",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "decimals": 2,
+ "mappings": [
+ {
+ "options": {
+ "match": "null",
+ "result": {
+ "text": "N/A"
+ }
+ },
+ "type": "special"
+ },
+ {
+ "options": {
+ "from": -1e+32,
+ "result": {
+ "text": "N/A"
+ },
+ "to": 0
+ },
+ "type": "range"
+ }
+ ],
+ "max": 100,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "rgba(50, 172, 45, 0.97)",
+ "value": null
+ },
+ {
+ "color": "rgba(237, 129, 40, 0.89)",
+ "value": 70
+ },
+ {
+ "color": "rgba(245, 54, 54, 0.9)",
+ "value": 90
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 6,
+ "x": 18,
+ "y": 1
+ },
+ "id": 75,
+ "maxDataPoints": 100,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "11.1.4",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"nonheap\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "",
+ "refId": "A",
+ "step": 14400
+ }
+ ],
+ "title": "Non-Heap used",
+ "type": "stat"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 4
+ },
+ "id": 140,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "I/O Overview",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "ops"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 0,
+ "y": 5
+ },
+ "id": 111,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\"}[1m]))",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "legendFormat": "HTTP",
+ "refId": "A"
+ }
+ ],
+ "title": "Rate",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "ops"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "HTTP"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#890f02",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "HTTP - 5xx"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#bf1b00",
+ "mode": "fixed"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 6,
+ "y": 5
+ },
+ "id": 112,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status=~\"5..\"}[1m]))",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "legendFormat": "HTTP - 5xx",
+ "refId": "A"
+ }
+ ],
+ "title": "Errors",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 12,
+ "y": 5
+ },
+ "id": 113,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(rate(http_server_requests_seconds_sum{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))/sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 1,
+ "legendFormat": "HTTP - AVG",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "max(http_server_requests_seconds_max{application=\"$application\", instance=\"$instance\", status!~\"5..\"})",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 1,
+ "legendFormat": "HTTP - MAX",
+ "refId": "B"
+ }
+ ],
+ "title": "Duration",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 18,
+ "y": 5
+ },
+ "id": 119,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "tomcat_threads_busy_threads{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 2,
+ "legendFormat": "TOMCAT - BSY",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "tomcat_threads_current_threads{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 2,
+ "legendFormat": "TOMCAT - CUR",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "tomcat_threads_config_max_threads{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 2,
+ "legendFormat": "TOMCAT - MAX",
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jetty_threads_busy{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 2,
+ "legendFormat": "JETTY - BSY",
+ "refId": "D"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jetty_threads_current{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 2,
+ "legendFormat": "JETTY - CUR",
+ "refId": "E"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jetty_threads_config_max{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 2,
+ "legendFormat": "JETTY - MAX",
+ "refId": "F"
+ }
+ ],
+ "title": "Utilisation",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 12
+ },
+ "id": 141,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "JVM Memory",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "bytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 0,
+ "y": 13
+ },
+ "id": 24,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "used",
+ "metric": "",
+ "refId": "A",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "committed",
+ "refId": "B",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "max",
+ "refId": "C",
+ "step": 2400
+ }
+ ],
+ "title": "JVM Heap",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "bytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 6,
+ "y": 13
+ },
+ "id": 25,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 2,
+ "legendFormat": "used",
+ "metric": "",
+ "refId": "A",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "committed",
+ "refId": "B",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "max",
+ "refId": "C",
+ "step": 2400
+ }
+ ],
+ "title": "JVM Non-Heap",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "bytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 12,
+ "y": 13
+ },
+ "id": 26,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "used",
+ "metric": "",
+ "refId": "A",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "committed",
+ "refId": "B",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "max",
+ "refId": "C",
+ "step": 2400
+ }
+ ],
+ "title": "JVM Total",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "bytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 18,
+ "y": 13
+ },
+ "id": 86,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_memory_vss_bytes{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": true,
+ "intervalFactor": 2,
+ "legendFormat": "vss",
+ "metric": "",
+ "refId": "A",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_memory_rss_bytes{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "rss",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_memory_swap_bytes{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "swap",
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_memory_rss_bytes{application=\"$application\", instance=\"$instance\"} + process_memory_swap_bytes{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "total",
+ "refId": "D"
+ }
+ ],
+ "title": "JVM Process Memory",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 20
+ },
+ "id": 142,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "JVM Misc",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "decimals": 1,
+ "mappings": [],
+ "max": 1,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percentunit"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 0,
+ "y": 21
+ },
+ "id": 106,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "system_cpu_usage{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 1,
+ "legendFormat": "system",
+ "metric": "",
+ "refId": "A",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_cpu_usage{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 1,
+ "legendFormat": "process",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "avg_over_time(process_cpu_usage{application=\"$application\", instance=\"$instance\"}[15m])",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 1,
+ "legendFormat": "process-15m",
+ "refId": "C"
+ }
+ ],
+ "title": "CPU Usage",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "decimals": 1,
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 6,
+ "y": 21
+ },
+ "id": 93,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "system_load_average_1m{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "system-1m",
+ "metric": "",
+ "refId": "A",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "system_cpu_count{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "cpus",
+ "refId": "B"
+ }
+ ],
+ "title": "Load",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "decimals": 0,
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 12,
+ "y": 21
+ },
+ "id": 32,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_threads_live_threads{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "live",
+ "metric": "",
+ "refId": "A",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_threads_daemon_threads{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "daemon",
+ "metric": "",
+ "refId": "B",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_threads_peak_threads{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "peak",
+ "refId": "C",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_threads{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 2,
+ "legendFormat": "process",
+ "refId": "D",
+ "step": 2400
+ }
+ ],
+ "title": "Threads",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "blocked"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#bf1b00",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "new"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#fce2de",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "runnable"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#7eb26d",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "terminated"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#511749",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "timed-waiting"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#c15c17",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "waiting"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#eab839",
+ "mode": "fixed"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 18,
+ "y": 21
+ },
+ "id": 124,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_threads_states_threads{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "{{state}}",
+ "refId": "A"
+ }
+ ],
+ "title": "Thread States",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "The percent of time spent on Garbage Collection over all CPUs assigned to the JVM process.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "decimals": 1,
+ "mappings": [],
+ "max": 1,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percentunit"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 0,
+ "y": 28
+ },
+ "id": 138,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "sum(rate(jvm_gc_pause_seconds_sum{application=\"$application\", instance=\"$instance\"}[1m])) by (application, instance) / on(application, instance) system_cpu_count",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "legendFormat": "CPU time spent on GC",
+ "refId": "A"
+ }
+ ],
+ "title": "GC Pressure",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "decimals": 0,
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "opm"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "debug"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#1F78C1",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "error"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#BF1B00",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "info"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#508642",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "trace"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#6ED0E0",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "warn"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "#EAB839",
+ "mode": "fixed"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 12,
+ "x": 6,
+ "y": 28
+ },
+ "id": 91,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "increase(logback_events_total{application=\"$application\", instance=\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 2,
+ "legendFormat": "{{level}}",
+ "metric": "",
+ "refId": "A",
+ "step": 1200
+ }
+ ],
+ "title": "Log Events",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "log": 10,
+ "type": "log"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "decimals": 0,
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 6,
+ "x": 18,
+ "y": 28
+ },
+ "id": 61,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_files_open_files{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 2,
+ "legendFormat": "open",
+ "metric": "",
+ "refId": "A",
+ "step": 2400
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "process_files_max_files{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 2,
+ "legendFormat": "max",
+ "metric": "",
+ "refId": "B",
+ "step": 2400
+ }
+ ],
+ "title": "File Descriptors",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 35
+ },
+ "id": 143,
+ "panels": [],
+ "repeat": "persistence_counts",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "JVM Memory Pools (Heap)",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "bytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 0,
+ "y": 36
+ },
+ "id": 3,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "repeat": "jvm_memory_pool_heap",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_heap\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 2,
+ "legendFormat": "used",
+ "metric": "",
+ "refId": "A",
+ "step": 1800
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_heap\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 2,
+ "legendFormat": "commited",
+ "metric": "",
+ "refId": "B",
+ "step": 1800
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_heap\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 2,
+ "legendFormat": "max",
+ "metric": "",
+ "refId": "C",
+ "step": 1800
+ }
+ ],
+ "title": "$jvm_memory_pool_heap",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 43
+ },
+ "id": 144,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "JVM Memory Pools (Non-Heap)",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "autoMigrateFrom": "graph",
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editable": true,
+ "error": false,
+ "fill": 1,
+ "grid": {
+ "leftLogBase": 1,
+ "rightLogBase": 1
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 0,
+ "y": 44
+ },
+ "id": 78,
+ "legend": {
+ "alignAsTable": false,
+ "avg": false,
+ "current": true,
+ "max": true,
+ "min": false,
+ "rightSide": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "maxPerRow": 3,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "repeat": "jvm_memory_pool_nonheap",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_nonheap\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 2,
+ "legendFormat": "used",
+ "metric": "",
+ "refId": "A",
+ "step": 1800
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_nonheap\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 2,
+ "legendFormat": "commited",
+ "metric": "",
+ "refId": "B",
+ "step": 1800
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_nonheap\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 2,
+ "legendFormat": "max",
+ "metric": "",
+ "refId": "C",
+ "step": 1800
+ }
+ ],
+ "thresholds": [],
+ "title": "$jvm_memory_pool_nonheap",
+ "tooltip": {
+ "msResolution": false,
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "timeseries",
+ "x-axis": true,
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "y-axis": true,
+ "y_formats": [
+ "mbytes",
+ "short"
+ ],
+ "yaxes": [
+ {
+ "format": "bytes",
+ "logBase": 1,
+ "min": 0,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ]
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 58
+ },
+ "id": 145,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Garbage Collection",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "autoMigrateFrom": "graph",
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 0,
+ "y": 59
+ },
+ "id": 98,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=\"$instance\"}[1m])",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 1,
+ "legendFormat": "{{action}} ({{cause}})",
+ "refId": "A"
+ }
+ ],
+ "thresholds": [],
+ "title": "Collections",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "timeseries",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ops",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": "",
+ "logBase": 1,
+ "show": true
+ }
+ ]
+ },
+ {
+ "aliasColors": {},
+ "autoMigrateFrom": "graph",
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 8,
+ "y": 59
+ },
+ "id": 101,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "rate(jvm_gc_pause_seconds_sum{application=\"$application\", instance=\"$instance\"}[1m])/rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=\"$instance\"}[1m])",
+ "format": "time_series",
+ "hide": false,
+ "instant": false,
+ "intervalFactor": 1,
+ "legendFormat": "avg {{action}} ({{cause}})",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_gc_pause_seconds_max{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "instant": false,
+ "intervalFactor": 1,
+ "legendFormat": "max {{action}} ({{cause}})",
+ "refId": "B"
+ }
+ ],
+ "thresholds": [],
+ "title": "Pause Durations",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "timeseries",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "s",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": "",
+ "logBase": 1,
+ "show": true
+ }
+ ]
+ },
+ {
+ "aliasColors": {},
+ "autoMigrateFrom": "graph",
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 16,
+ "y": 59
+ },
+ "id": 99,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "rate(jvm_gc_memory_allocated_bytes_total{application=\"$application\", instance=\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "allocated",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "rate(jvm_gc_memory_promoted_bytes_total{application=\"$application\", instance=\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "promoted",
+ "refId": "B"
+ }
+ ],
+ "thresholds": [],
+ "title": "Allocated/Promoted",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "timeseries",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "Bps",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ]
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 66
+ },
+ "id": 146,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Classloading",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "autoMigrateFrom": "graph",
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editable": true,
+ "error": false,
+ "fill": 1,
+ "grid": {
+ "leftLogBase": 1,
+ "rightLogBase": 1
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 12,
+ "x": 0,
+ "y": 67
+ },
+ "id": 37,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_classes_loaded_classes{application=\"$application\", instance=\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "loaded",
+ "metric": "",
+ "refId": "A",
+ "step": 1200
+ }
+ ],
+ "thresholds": [],
+ "title": "Classes loaded",
+ "tooltip": {
+ "msResolution": false,
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "timeseries",
+ "x-axis": true,
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "y-axis": true,
+ "y_formats": [
+ "short",
+ "short"
+ ],
+ "yaxes": [
+ {
+ "format": "short",
+ "logBase": 1,
+ "min": 0,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ]
+ },
+ {
+ "aliasColors": {},
+ "autoMigrateFrom": "graph",
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editable": true,
+ "error": false,
+ "fill": 1,
+ "grid": {
+ "leftLogBase": 1,
+ "rightLogBase": 1
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 12,
+ "x": 12,
+ "y": 67
+ },
+ "id": 38,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "delta(jvm_classes_loaded_classes{application=\"$application\",instance=\"$instance\"}[1m])",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "delta-1m",
+ "metric": "",
+ "refId": "A",
+ "step": 1200
+ }
+ ],
+ "thresholds": [],
+ "title": "Class delta",
+ "tooltip": {
+ "msResolution": false,
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "timeseries",
+ "x-axis": true,
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "y-axis": true,
+ "y_formats": [
+ "ops",
+ "short"
+ ],
+ "yaxes": [
+ {
+ "format": "short",
+ "label": "",
+ "logBase": 1,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ]
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 74
+ },
+ "id": 147,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Buffer Pools",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "autoMigrateFrom": "graph",
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 0,
+ "y": 75
+ },
+ "id": 131,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "maxPerRow": 3,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "repeat": "jvm_buffer_pool",
+ "seriesOverrides": [
+ {
+ "alias": "count",
+ "yaxis": 2
+ },
+ {
+ "alias": "buffers",
+ "yaxis": 2
+ }
+ ],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_buffer_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_buffer_pool\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "used",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_buffer_total_capacity_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_buffer_pool\"}",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "capacity",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "expr": "jvm_buffer_count_buffers{application=\"$application\", instance=\"$instance\", id=~\"$jvm_buffer_pool\"}",
+ "format": "time_series",
+ "hide": false,
+ "intervalFactor": 2,
+ "legendFormat": "buffers",
+ "refId": "C"
+ }
+ ],
+ "thresholds": [],
+ "title": "$jvm_buffer_pool",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "timeseries",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "decbytes",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "decimals": 0,
+ "format": "short",
+ "label": "",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ }
+ ]
+ }
+ ],
+ "refresh": "auto",
+ "schemaVersion": 39,
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "current": {
+ "isNone": true,
+ "selected": false,
+ "text": "None",
+ "value": ""
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "",
+ "hide": 0,
+ "includeAll": false,
+ "label": "Application",
+ "multi": false,
+ "name": "application",
+ "options": [],
+ "query": "label_values(application)",
+ "refresh": 2,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "allFormat": "glob",
+ "current": {
+ "selected": false,
+ "text": "docker-compose-cps-and-ncmp-1:8080",
+ "value": "docker-compose-cps-and-ncmp-1:8080"
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "",
+ "hide": 0,
+ "includeAll": false,
+ "label": "Instance",
+ "multi": false,
+ "multiFormat": "glob",
+ "name": "instance",
+ "options": [],
+ "query": "label_values(jvm_memory_used_bytes{application=\"$application\"}, instance)",
+ "refresh": 2,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "allFormat": "glob",
+ "current": {
+ "selected": false,
+ "text": "All",
+ "value": "$__all"
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "",
+ "hide": 2,
+ "includeAll": true,
+ "label": "JVM Memory Pools Heap",
+ "multi": false,
+ "multiFormat": "glob",
+ "name": "jvm_memory_pool_heap",
+ "options": [],
+ "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"},id)",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 1,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "allFormat": "glob",
+ "current": {
+ "selected": false,
+ "text": "All",
+ "value": "$__all"
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "",
+ "hide": 2,
+ "includeAll": true,
+ "label": "JVM Memory Pools Non-Heap",
+ "multi": false,
+ "multiFormat": "glob",
+ "name": "jvm_memory_pool_nonheap",
+ "options": [],
+ "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"},id)",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 2,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "allFormat": "glob",
+ "current": {
+ "selected": false,
+ "text": "All",
+ "value": "$__all"
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "",
+ "hide": 2,
+ "includeAll": true,
+ "label": "JVM Buffer Pools",
+ "multi": false,
+ "multiFormat": "glob",
+ "name": "jvm_buffer_pool",
+ "options": [],
+ "query": "label_values(jvm_buffer_memory_used_bytes{application=\"$application\", instance=\"$instance\"},id)",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 1,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ }
+ ]
+ },
+ "time": {
+ "from": "now-30m",
+ "to": "now"
+ },
+ "timepicker": {
+ "now": true,
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "browser",
+ "title": "JVM (Micrometer)",
+ "uid": "bdvp1kgecrda8f",
+ "version": 1,
+ "weekStart": ""
+}
diff --git a/docker-compose/config/grafana/provisioning/dashboards/dashboard.yml b/docker-compose/config/grafana/provisioning/dashboards/dashboard.yml
new file mode 100644
index 0000000000..b6dba86680
--- /dev/null
+++ b/docker-compose/config/grafana/provisioning/dashboards/dashboard.yml
@@ -0,0 +1,9 @@
+apiVersion: 1
+
+providers:
+ - name: default
+ orgId: 1
+ type: file
+ options:
+ path: /var/lib/grafana/dashboards
+ foldersFromFilesStructure: true
diff --git a/docker-compose/config/grafana/provisioning/datasources/datasource.yml b/docker-compose/config/grafana/provisioning/datasources/datasource.yml
new file mode 100644
index 0000000000..86fd3465e1
--- /dev/null
+++ b/docker-compose/config/grafana/provisioning/datasources/datasource.yml
@@ -0,0 +1,8 @@
+apiVersion: 1
+
+datasources:
+ - name: Prometheus
+ type: prometheus
+ access: proxy
+ url: http://prometheus:9090
+ isDefault: true
diff --git a/docker-compose/nginx.conf b/docker-compose/config/nginx/nginx.conf
index 17fad1b876..6e9d102fed 100644
--- a/docker-compose/nginx.conf
+++ b/docker-compose/config/nginx/nginx.conf
@@ -20,6 +20,7 @@ http {
# Add more server entries here for scaling or load balancing
upstream cps-and-ncmp {
+ least_conn;
server cps-and-ncmp:8080;
}
diff --git a/docker-compose/proxy_params b/docker-compose/config/nginx/proxy_params
index ab092a98f3..ab092a98f3 100644
--- a/docker-compose/proxy_params
+++ b/docker-compose/config/nginx/proxy_params
diff --git a/docker-compose/postgres-init.sql b/docker-compose/config/postgres-init.sql
index 0c96de5b55..0c96de5b55 100644
--- a/docker-compose/postgres-init.sql
+++ b/docker-compose/config/postgres-init.sql
diff --git a/docker-compose/config/prometheus.yml b/docker-compose/config/prometheus.yml
new file mode 100644
index 0000000000..e1aa4763d9
--- /dev/null
+++ b/docker-compose/config/prometheus.yml
@@ -0,0 +1,14 @@
+global:
+ scrape_interval: 5s
+ evaluation_interval: 5s
+
+scrape_configs:
+- job_name: 'cps-and-ncmp'
+ metrics_path: '/actuator/prometheus'
+ scrape_interval: 5s
+ static_configs:
+ # If you want to inject Prometheus into particular containers, the targets should be provided similar to the following examples:
+ # - 'docker-compose-cps-and-ncmp-1:8080'
+ # - 'docker-compose-cps-and-ncmp-2:8080'
+ - targets:
+ - 'cps-and-ncmp:8080' \ No newline at end of file
diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml
index 2ecd456fc5..b854064ca5 100644
--- a/docker-compose/docker-compose.yml
+++ b/docker-compose/docker-compose.yml
@@ -20,7 +20,10 @@ services:
### docker-compose --profile dmi-service up -d -> run CPS services incl. dmi-plugin ###
### docker-compose --profile dmi-stub --profile monitoring up -d -> run CPS with stubbed dmi-plugin (for registration performance testing)
+ ### docker-compose --profile dmi-stub --profile tracing up -d -> run CPS with stubbed dmi-plugin (for open telemetry tracing testing make ONAP_TRACING_ENABLED "true" later "http://localhost:16686" can be accessed from browser)
+ ### docker-compose --profile dmi-stub --profile policy-executor-stub up -d -> run CPS with stubbed dmi-plugin and policy executor stub (for policy executor service testing make POLICY_SERVICE_ENABLED "true")
### to disable notifications make notification.enabled to false & comment out kafka/zookeeper services ###
+ ### DEBUG: Look for '### DEBUG' comments to enable CPS-NCMP debugging
dbpostgresql:
container_name: dbpostgresql
@@ -32,7 +35,7 @@ services:
POSTGRES_USER: ${DB_USERNAME:-cps}
POSTGRES_PASSWORD: ${DB_PASSWORD:-cps}
volumes:
- - ./postgres-init.sql:/docker-entrypoint-initdb.d/postgres-init.sql
+ - ./config/postgres-init.sql:/docker-entrypoint-initdb.d/postgres-init.sql
deploy:
resources:
reservations:
@@ -54,18 +57,27 @@ services:
DMI_PASSWORD: ${DMI_PASSWORD:-cpsr0cks!}
KAFKA_BOOTSTRAP_SERVER: kafka:29092
notification.enabled: 'true'
+ ONAP_TRACING_ENABLED: 'false'
+ ONAP_OTEL_SAMPLER_JAEGER_REMOTE_ENDPOINT: http://jaeger-service:14250
+ ONAP_OTEL_EXPORTER_ENDPOINT: http://jaeger-service:4317
+ POLICY_SERVICE_ENABLED: 'false'
+ POLICY_SERVICE_DEFAULT_DECISION: 'deny from env'
+ JAVA_TOOL_OPTIONS: "-XX:InitialRAMPercentage=75.0 -XX:MaxRAMPercentage=75.0"
+ ### DEBUG: Uncomment next line to enable java debugging
+ ### DEBUG: JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
restart: unless-stopped
depends_on:
- dbpostgresql
deploy:
+ ### DEBUG: For easier debugging use just 1 instance (also update docker-compose/config/nginx/nginx.conf !)
replicas: 2
resources:
- reservations:
- cpus: '2'
- memory: 2G
limits:
cpus: '3'
- memory: 3G
+ memory: 2G
+ ### DEBUG: Uncomment next 2 lines to enable java debugging (ensure 'ports' aligns with 'deploy')
+ ### DEBUG ports:
+ ### DEBUG - ${CPS_CORE_DEBUG_PORT:-5005}:5005
nginx:
container_name: nginx-loadbalancer
@@ -75,8 +87,8 @@ services:
depends_on:
- cps-and-ncmp
volumes:
- - ./nginx.conf:/etc/nginx/nginx.conf
- - ./proxy_params:/etc/nginx/proxy_params
+ - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf
+ - ./config/nginx/proxy_params:/etc/nginx/proxy_params
### if kafka is not required comment out zookeeper and kafka ###
zookeeper:
@@ -135,9 +147,11 @@ services:
KAFKA_BOOTSTRAP_SERVER: kafka:29092
NCMP_CONSUMER_GROUP_ID: ncmp-group
NCMP_ASYNC_M2M_TOPIC: ncmp-async-m2m
+ MODULE_INITIAL_PROCESSING_DELAY_MS: 120000
MODULE_REFERENCES_DELAY_MS: 100
MODULE_RESOURCES_DELAY_MS: 1000
- DATA_FOR_CM_HANDLE_DELAY_MS: 2500
+ READ_DATA_FOR_CM_HANDLE_DELAY_MS: 300
+ WRITE_DATA_FOR_CM_HANDLE_DELAY_MS: 670
restart: unless-stopped
profiles:
- dmi-stub
@@ -147,32 +161,34 @@ services:
container_name: policy-executor-stub
image: ${DOCKER_REPO:-nexus3.onap.org:10003}/onap/policy-executor-stub:latest
ports:
- - 8785:8080
+ - 8785:8093
restart: unless-stopped
profiles:
- policy-executor-stub
prometheus:
- container_name: prometheus-container
+ container_name: prometheus
image: prom/prometheus:latest
ports:
- 9090:9090
restart: always
volumes:
- - ./prometheus.yml:/etc/prometheus/prometheus.yml
+ - ./config/prometheus.yml:/etc/prometheus/prometheus.yml
profiles:
- monitoring
grafana:
image: grafana/grafana-oss:latest
user: ""
- container_name: grafana-container
+ container_name: grafana
depends_on:
prometheus:
condition: service_started
ports:
- 3000:3000
volumes:
+ - ./config/grafana/provisioning/:/etc/grafana/provisioning/
+ - ./config/grafana/jvm-micrometer-dashboard.json:/var/lib/grafana/dashboards/jvm-micrometer-dashboard.json
- grafana:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
@@ -180,5 +196,26 @@ services:
profiles:
- monitoring
+ kafka-ui:
+ container_name: kafka-ui
+ image: provectuslabs/kafka-ui:latest
+ ports:
+ - 8089:8080
+ environment:
+ DYNAMIC_CONFIG_ENABLED: 'true'
+ KAFKA_CLUSTERS_0_NAME: 'cps-kafka-local'
+ KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092
+ profiles:
+ - monitoring
+
+ jaeger-service:
+ container_name: jaeger-service
+ image: jaegertracing/all-in-one:latest
+ ports:
+ - 16686:16686
+ restart: unless-stopped
+ profiles:
+ - tracing
+
volumes:
grafana:
diff --git a/docker-compose/prometheus.yml b/docker-compose/prometheus.yml
deleted file mode 100644
index 9640709089..0000000000
--- a/docker-compose/prometheus.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-global:
- scrape_interval: 5s
- evaluation_interval: 5s
-
-scrape_configs:
- - job_name: 'cps-and-ncm'
- metrics_path: '/actuator/prometheus'
- scrape_interval: 5s
- static_configs:
- - targets: ['cps-and-ncmp:8080']
diff --git a/docs/_static/cps-delta-mechanism.png b/docs/_static/cps-delta-mechanism.png
index ab78ad9a1b..07923d4c26 100644
--- a/docs/_static/cps-delta-mechanism.png
+++ b/docs/_static/cps-delta-mechanism.png
Binary files differ
diff --git a/docs/_static/logo_onap_2024.png b/docs/_static/logo_onap_2024.png
new file mode 100644
index 0000000000..55d307fc34
--- /dev/null
+++ b/docs/_static/logo_onap_2024.png
Binary files differ
diff --git a/docs/admin-guide.rst b/docs/admin-guide.rst
index 1c4d7455f0..4a40f9b29c 100644
--- a/docs/admin-guide.rst
+++ b/docs/admin-guide.rst
@@ -111,9 +111,9 @@ Execute CPS service that you want to calculate total elapsed time and log as sho
.. code-block::
- 2022-01-28 18:39:17.679 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : DataspaceRepository.getByName() with argument[s] = [test42] having result = org.onap.cps.spi.entities.DataspaceEntity@68ded236 :: 205 ms
+ 2022-01-28 18:39:17.679 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : DataspaceRepository.getByName() with argument[s] = [test42] having result = org.onap.cps.impl.models.DataspaceEntity@68ded236 :: 205 ms
- 2022-01-28 18:39:17.726 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : AnchorRepository.getByDataspaceAndName() with argument[s] = [org.onap.cps.spi.entities.DataspaceEntity@68ded236, bookstore] having result = org.onap.cps.spi.entities.AnchorEntity@71c47fb1 :: 46 ms
+ 2022-01-28 18:39:17.726 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : AnchorRepository.getByDataspaceAndName() with argument[s] = [org.onap.cps.impl.models.DataspaceEntity@68ded236, bookstore] having result = org.onap.cps.impl.models.AnchorEntity@71c47fb1 :: 46 ms
2022-01-28 18:39:17.768 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : CpsAdminPersistenceServiceImpl.getAnchor() with argument[s] = [test42, bookstore] having result = Anchor(name=bookstore, dataspaceName=test42, schemaSetName=bookstore) :: 299 ms
diff --git a/docs/api/swagger/cps/openapi.yaml b/docs/api/swagger/cps/openapi.yaml
index 7cba5816f2..3069b18d1e 100644
--- a/docs/api/swagger/cps/openapi.yaml
+++ b/docs/api/swagger/cps/openapi.yaml
@@ -9,11 +9,7 @@ info:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0
title: ONAP Open API v3 Configuration Persistence Service
- version: 1.0.0
- x-planned-retirement-date: "202212"
- x-component: Modeling
- x-logo:
- url: cps_logo.png
+ version: 3.5.4
servers:
- url: /cps/api
security:
@@ -40,7 +36,7 @@ paths:
responses:
"201":
content:
- text/plain:
+ application/json:
schema:
example: my-resource
type: string
@@ -364,7 +360,7 @@ paths:
responses:
"201":
content:
- text/plain:
+ application/json:
schema:
example: my-resource
type: string
@@ -711,7 +707,7 @@ paths:
responses:
"201":
content:
- text/plain:
+ application/json:
schema:
example: my-resource
type: string
@@ -1346,7 +1342,7 @@ paths:
schema:
example: 2021-03-21T00:10:34.030-0100
type: string
- - description: Content type header
+ - description: Content type in header
in: header
name: Content-Type
required: true
@@ -1455,6 +1451,15 @@ paths:
schema:
default: /
type: string
+ - description: "Boolean flag to validate data, without persisting it. Default\
+ \ value is set to false."
+ in: query
+ name: dry-run
+ required: false
+ schema:
+ default: false
+ example: false
+ type: boolean
- description: observed-timestamp
in: query
name: observed-timestamp
@@ -1462,7 +1467,7 @@ paths:
schema:
example: 2021-03-21T00:10:34.030-0100
type: string
- - description: Content type header
+ - description: Content type in header
in: header
name: Content-Type
required: true
@@ -1491,7 +1496,7 @@ paths:
responses:
"201":
content:
- text/plain:
+ application/json:
schema:
example: my-resource
type: string
@@ -1587,6 +1592,13 @@ paths:
schema:
example: 2021-03-21T00:10:34.030-0100
type: string
+ - description: Content type in header
+ in: header
+ name: Content-Type
+ required: true
+ schema:
+ example: application/json
+ type: string
requestBody:
content:
application/json:
@@ -1595,7 +1607,16 @@ paths:
$ref: '#/components/examples/dataSample'
value: null
schema:
+ type: string
+ application/xml:
+ examples:
+ dataSample:
+ $ref: '#/components/examples/dataSampleXml'
+ value: null
+ schema:
type: object
+ xml:
+ name: stores
required: true
responses:
"200":
@@ -1762,6 +1783,13 @@ paths:
schema:
example: 2021-03-21T00:10:34.030-0100
type: string
+ - description: Content type in header
+ in: header
+ name: Content-Type
+ required: true
+ schema:
+ example: application/json
+ type: string
requestBody:
content:
application/json:
@@ -1770,12 +1798,21 @@ paths:
$ref: '#/components/examples/dataSample'
value: null
schema:
+ type: string
+ application/xml:
+ examples:
+ dataSample:
+ $ref: '#/components/examples/dataSampleXml'
+ value: null
+ schema:
type: object
+ xml:
+ name: stores
required: true
responses:
"201":
content:
- text/plain:
+ application/json:
schema:
example: my-resource
type: string
@@ -1912,7 +1949,7 @@ paths:
summary: Replace list content
tags:
- cps-data
- /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/deltaAnchors:
+ /v2/dataspaces/{dataspace-name}/anchors/{source-anchor-name}/delta:
get:
description: Get delta between two anchors within a given dataspace
operationId: getDeltaByDataspaceAndAnchors
@@ -1924,9 +1961,9 @@ paths:
schema:
example: my-dataspace
type: string
- - description: anchor-name
+ - description: source-anchor-name
in: path
- name: anchor-name
+ name: source-anchor-name
required: true
schema:
example: my-anchor
@@ -2005,7 +2042,6 @@ paths:
tags:
- cps-data
x-codegen-request-body-name: xpath
- /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/deltaPayload:
post:
description: Get delta between an anchor in a dataspace and JSON payload
operationId: getDeltaByDataspaceAnchorAndPayload
@@ -2017,9 +2053,9 @@ paths:
schema:
example: my-dataspace
type: string
- - description: anchor-name
+ - description: source-anchor-name
in: path
- name: anchor-name
+ name: source-anchor-name
required: true
schema:
example: my-anchor
@@ -2379,17 +2415,17 @@ components:
<code>2</code> <name>kids</name> </categories> </bookstore> </stores>
deltaReportSample:
value:
- - action: ADD
+ - action: create
xpath: "/bookstore/categories/[@code=3]"
target-data:
code: "3,"
name: kidz
- - action: REMOVE
+ - action: remove
xpath: "/bookstore/categories/[@code=1]"
source-data:
code: "1,"
name: Fiction
- - action: UPDATE
+ - action: replace
xpath: "/bookstore/categories/[@code=2]"
source-data:
name: Funny
@@ -2515,14 +2551,24 @@ components:
schema:
example: 2021-03-21T00:10:34.030-0100
type: string
- contentTypeHeader:
- description: Content type header
+ contentTypeInHeader:
+ description: Content type in header
in: header
name: Content-Type
required: true
schema:
example: application/json
type: string
+ dryRunInQuery:
+ description: "Boolean flag to validate data, without persisting it. Default\
+ \ value is set to false."
+ in: query
+ name: dry-run
+ required: false
+ schema:
+ default: false
+ example: false
+ type: boolean
requiredXpathInQuery:
description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/xpath.html"
examples:
@@ -2535,6 +2581,14 @@ components:
required: true
schema:
type: string
+ sourceAnchorNameInPath:
+ description: source-anchor-name
+ in: path
+ name: source-anchor-name
+ required: true
+ schema:
+ example: my-anchor
+ type: string
targetAnchorNameInQuery:
description: target-anchor-name
in: query
@@ -2577,7 +2631,7 @@ components:
responses:
Created:
content:
- text/plain:
+ application/json:
schema:
example: my-resource
type: string
diff --git a/docs/api/swagger/ncmp/openapi-inventory.yaml b/docs/api/swagger/ncmp/openapi-inventory.yaml
index 4f5180d510..a2c7af6fda 100644
--- a/docs/api/swagger/ncmp/openapi-inventory.yaml
+++ b/docs/api/swagger/ncmp/openapi-inventory.yaml
@@ -2,7 +2,7 @@ openapi: 3.0.3
info:
description: NCMP Inventory API
title: NCMP Inventory API
- version: "1.0"
+ version: 3.5.4
servers:
- url: /ncmpInventory
security:
diff --git a/docs/api/swagger/ncmp/openapi.yaml b/docs/api/swagger/ncmp/openapi.yaml
index 7b33fa156b..f93395a6db 100644
--- a/docs/api/swagger/ncmp/openapi.yaml
+++ b/docs/api/swagger/ncmp/openapi.yaml
@@ -2,7 +2,7 @@ openapi: 3.0.3
info:
description: NCMP to CPS Proxy API
title: NCMP to CPS Proxy API
- version: "1.0"
+ version: 3.5.4
servers:
- url: /ncmp
security:
@@ -20,13 +20,14 @@ paths:
schema:
example: ncmp-datastore:running
type: string
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
- description: The format of resource identifier depend on the associated DMI
Plugin implementation. For ONAP DMI Plugin it will be RESTConf paths but
@@ -130,13 +131,14 @@ paths:
schema:
example: ncmp-datastore:running
type: string
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
- description: The format of resource identifier depend on the associated DMI
Plugin implementation. For ONAP DMI Plugin it will be RESTConf paths but
@@ -264,13 +266,14 @@ paths:
schema:
example: ncmp-datastore:running
type: string
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
- description: The format of resource identifier depend on the associated DMI
Plugin implementation. For ONAP DMI Plugin it will be RESTConf paths but
@@ -377,13 +380,14 @@ paths:
schema:
example: ncmp-datastore:running
type: string
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
- description: The format of resource identifier depend on the associated DMI
Plugin implementation. For ONAP DMI Plugin it will be RESTConf paths but
@@ -495,13 +499,14 @@ paths:
schema:
example: ncmp-datastore:running
type: string
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
- description: The format of resource identifier depend on the associated DMI
Plugin implementation. For ONAP DMI Plugin it will be RESTConf paths but
@@ -608,7 +613,7 @@ paths:
post:
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. A maximum of 50 cm handles per operation
+ to identify the relevant messages. A maximum of 200 cm handles per operation
is supported.
operationId: executeDataOperationForCmHandles
parameters:
@@ -824,13 +829,14 @@ paths:
handle
operationId: getModuleReferencesByCmHandle
parameters:
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
responses:
"200":
@@ -880,13 +886,14 @@ paths:
\ with options to filter on module name and revision"
operationId: getModuleDefinitions
parameters:
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
- description: Filter for a module name.This is an optional parameter
in: query
@@ -1017,13 +1024,14 @@ paths:
description: Retrieve CM handle details and properties by cm handle id
operationId: retrieveCmHandleDetailsById
parameters:
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
responses:
"200":
@@ -1070,13 +1078,14 @@ paths:
description: Get CM handle properties by cm handle id
operationId: getCmHandlePublicPropertiesByCmHandleId
parameters:
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
responses:
"200":
@@ -1210,13 +1219,14 @@ paths:
description: Get CM handle state by cm handle id
operationId: getCmHandleStateByCmHandleId
parameters:
- - description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ - description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network\
+ \ CM Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
responses:
"200":
@@ -1464,14 +1474,15 @@ components:
schema:
example: ncmp-datastore:running
type: string
- cmHandleInPath:
- description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
+ cmHandleReferenceInPath:
+ description: "The identifier (cmHandle or alternate) for a network function,\
+ \ network element, subnetwork or any other cm object by managed Network CM\
+ \ Proxy"
in: path
name: cm-handle
required: true
schema:
- example: my-cm-handle
+ example: my-cm-handle-reference
type: string
resourceIdentifierInQuery:
description: The format of resource identifier depend on the associated DMI
@@ -1558,6 +1569,15 @@ components:
required: true
schema:
type: string
+ cmHandleInPath:
+ description: "The identifier for a network function, network element, subnetwork\
+ \ or any other cm object by managed Network CM Proxy"
+ in: path
+ name: cm-handle
+ required: true
+ schema:
+ example: my-cm-handle
+ type: string
cpsPathInQuery:
description: "For more details on cps path, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
examples:
diff --git a/docs/api/swagger/policy-executor/openapi.yaml b/docs/api/swagger/policy-executor/openapi.yaml
index 98c5b1e79a..1248c0d08b 100644
--- a/docs/api/swagger/policy-executor/openapi.yaml
+++ b/docs/api/swagger/policy-executor/openapi.yaml
@@ -18,55 +18,170 @@
openapi: 3.0.3
info:
+ description: Allows NCMP to execute a policy defined by a third party implementation
+ before proceeding with a CM operation
title: Policy Executor
- description: "Allows NCMP to execute a policy defined by a third party implementation before proceeding with a CM operation"
version: 1.0.0
servers:
- - url: /policy-executor/api
+- url: /
+security:
+- bearerAuth: []
tags:
- - name: policy-executor
- description: "Execute all your policies"
+- description: Execute all your policies
+ name: policy-executor
paths:
- /v1/{action}:
+ /policy-executor/api/v1/{action}:
post:
- description: "Fire a Policy action"
+ description: Fire a Policy action
operationId: executePolicyAction
parameters:
- - $ref: '#/components/parameters/authorizationInHeader'
- - $ref: '#/components/parameters/actionInPath'
- requestBody:
+ - description: Bearer token may be used to identify client as part of a policy
+ explode: false
+ in: header
+ name: Authorization
+ required: false
+ schema:
+ type: string
+ style: simple
+ - description: "The policy action. Currently supported options: 'execute'"
+ explode: false
+ in: path
+ name: action
required: true
- description: "The action request body"
+ schema:
+ example: execute
+ type: string
+ style: simple
+ requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PolicyExecutionRequest'
- tags:
- - policy-executor
+ description: The action request body
+ required: true
responses:
- '200':
- description: "Successful policy execution"
+ "200":
content:
application/json:
schema:
$ref: '#/components/schemas/PolicyExecutionResponse'
- '400':
- $ref: '#/components/responses/BadRequest'
- '403':
- $ref: '#/components/responses/Forbidden'
- '500':
- $ref: '#/components/responses/InternalServerError'
-
+ description: Successful policy execution
+ "400":
+ content:
+ application/json:
+ example:
+ status: 400
+ message: Bad Request
+ details: The provided request is not valid
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Bad request
+ "401":
+ content:
+ application/json:
+ example:
+ status: 401
+ message: Unauthorized request
+ details: This request is unauthorized
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Unauthorized request
+ "403":
+ content:
+ application/json:
+ example:
+ status: 403
+ message: Request Forbidden
+ details: This request is forbidden
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Request forbidden
+ "500":
+ content:
+ application/json:
+ example:
+ status: 500
+ message: Internal Server Error
+ details: Internal server error occurred
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Internal server error
+ tags:
+ - policy-executor
components:
- securitySchemes:
- bearerAuth:
- type: http
- description: "Bearer token (from client that called CPS-NCMP),used by policies to identify the client"
- scheme: bearer
+ parameters:
+ actionInPath:
+ description: "The policy action. Currently supported options: 'execute'"
+ explode: false
+ in: path
+ name: action
+ required: true
+ schema:
+ example: execute
+ type: string
+ style: simple
+ authorizationInHeader:
+ description: Bearer token may be used to identify client as part of a policy
+ explode: false
+ in: header
+ name: Authorization
+ required: false
+ schema:
+ type: string
+ style: simple
+ responses:
+ BadRequest:
+ content:
+ application/json:
+ example:
+ status: 400
+ message: Bad Request
+ details: The provided request is not valid
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Bad request
+ Unauthorized:
+ content:
+ application/json:
+ example:
+ status: 401
+ message: Unauthorized request
+ details: This request is unauthorized
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Unauthorized request
+ Forbidden:
+ content:
+ application/json:
+ example:
+ status: 403
+ message: Request Forbidden
+ details: This request is forbidden
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Request forbidden
+ InternalServerError:
+ content:
+ application/json:
+ example:
+ status: 500
+ message: Internal Server Error
+ details: Internal server error occurred
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Internal server error
+ NotImplemented:
+ content:
+ application/json:
+ example:
+ status: 501
+ message: Not Implemented
+ details: Method not implemented
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Method not (yet) implemented
schemas:
ErrorMessage:
- type: object
- title: Error
properties:
status:
type: string
@@ -74,149 +189,73 @@ components:
type: string
details:
type: string
-
- Payload:
+ title: Error
type: object
+ Request:
+ example:
+ schema: org.onap.cps.ncmp.policy-executor:ncmp-create-schema:1.0.0
+ data: "{}"
properties:
- targetFdn:
- type: string
- description: "The complete FDN (Fully Distinguished Name) for the element to be changed"
- example: "/Subnetwork=Ireland/MeContext=Athlone/ManagedElement=Athlone/SomeFunction=1/Cell=12"
- cmHandleId:
- type: string
- description: "The CM handle ID (optional)"
- example: "F811AF64F5146DFC545EC60B73DE948E"
- resourceIdentifier:
+ schema:
+ description: The schema for the data in this request. The schema name should
+ include the type of operation
+ example: org.onap.cps.ncmp.policy-executor:ncmp-create-schema:1.0.0
type: string
- description: "The resource identifier (optional)"
- example: "ManagedElement=Athlone/SomeFunction=1/Cell=12"
- cmChangeRequest:
+ data:
+ description: The data related to the request. The format of the object is
+ determined by the schema
type: object
- description: "The content of the change to be made"
- example: '{"Cell":[{"id":"Cell-id","attributes":{"administrativeState":"UNLOCKED"}}]}'
required:
- - targetFdn
- - cmChangeRequest
-
- PolicyExecutionRequest:
+ - data
+ - schema
type: object
+ PolicyExecutionRequest:
+ example:
+ decisionType: allow
+ requests:
+ - schema: org.onap.cps.ncmp.policy-executor:ncmp-create-schema:1.0.0
+ data: "{}"
+ - schema: org.onap.cps.ncmp.policy-executor:ncmp-create-schema:1.0.0
+ data: "{}"
properties:
- payloadType:
- type: string
- description: "The type of payload. Currently supported options: 'cm_write'"
- example: "cm_write"
decisionType:
+ description: "The type of decision. Currently supported options: 'allow'"
+ example: allow
type: string
- description: "The type of decision. Currently supported options: 'permit'"
- example: "permit"
- payload:
- type: array
+ requests:
items:
- $ref: '#/components/schemas/Payload'
+ $ref: '#/components/schemas/Request'
+ type: array
required:
- - payloadType
- - decisionType
- - payload
-
- PolicyExecutionResponse:
+ - decisionType
+ - requests
type: object
+ PolicyExecutionResponse:
+ example:
+ decision: deny
+ decisionId: 550e8400-e29b-41d4-a716-446655440000
+ message: Object locked due to recent change
properties:
decisionId:
+ description: Unique ID for the decision (for auditing purposes)
+ example: 550e8400-e29b-41d4-a716-446655440000
type: string
- description: "Unique ID for the decision (for auditing purposes)"
- example: "550e8400-e29b-41d4-a716-446655440000"
decision:
+ description: "The decision outcome. Currently supported values: 'allow','deny'"
+ example: deny
type: string
- description: "The decision outcome. Currently supported values: 'permit','deny'"
- example: "deny"
message:
+ description: Additional information regarding the decision outcome
+ example: Object locked due to recent change
type: string
- description: "Additional information regarding the decision outcome"
- example: "Object locked due to recent change"
required:
- - decisionId
- - decision
- - message
-
- responses:
- NotFound:
- description: "The specified resource was not found"
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 404
- message: "Resource Not Found"
- details: "The requested resource is not found"
- Unauthorized:
- description: "Unauthorized request"
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 401
- message: "Unauthorized request"
- details: "This request is unauthorized"
- Forbidden:
- description: "Request forbidden"
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 403
- message: "Request Forbidden"
- details: "This request is forbidden"
- BadRequest:
- description: "Bad request"
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 400
- message: "Bad Request"
- details: "The provided request is not valid"
-
- InternalServerError:
- description: "Internal server error"
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 500
- message: "Internal Server Error"
- details: "Internal server error occurred"
-
- NotImplemented:
- description: "Method not (yet) implemented"
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 501
- message: "Not Implemented"
- details: "Method not implemented"
-
- parameters:
- actionInPath:
- name: action
- in: path
- description: "The policy action. Currently supported options: 'execute'"
- required: true
- schema:
- type: string
- example: "execute"
- authorizationInHeader:
- name: Authorization
- in: header
- description: "Bearer token may be used to identify client as part of a policy"
- schema:
- type: string
-
-security:
- - bearerAuth: []
+ - decision
+ - decisionId
+ - message
+ type: object
+ securitySchemes:
+ bearerAuth:
+ description: "Bearer token (from client that called CPS-NCMP),used by policies\
+ \ to identify the client"
+ scheme: bearer
+ type: http
diff --git a/docs/architecture.rst b/docs/architecture.rst
index d21b213bb8..3cb0a26ce7 100644
--- a/docs/architecture.rst
+++ b/docs/architecture.rst
@@ -19,7 +19,7 @@ parameters that need to be used by ONAP.
CPS is no longer a stand alone component and is released along with and the NCMP-DMI-Plugin.
Project page describing eventual scope and ambition is here:
-`Configuration Persistence Service Project <https://wiki.onap.org/display/DW/Configuration+Persistence+Service+Project>`_
+`Configuration Persistence Service Project <https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16398157/Configuration+Persistence+Service+Project>`_
This page reflects the state for the latest release.
diff --git a/docs/cm-notification-subscriptions.rst b/docs/cm-notification-subscriptions.rst
new file mode 100644
index 0000000000..14e871addc
--- /dev/null
+++ b/docs/cm-notification-subscriptions.rst
@@ -0,0 +1,48 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. Copyright (C) 2024 Nordix Foundation
+
+.. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
+.. _cmNotificationSubscriptions:
+
+
+CM Data Subscriptions
+#####################
+
+.. toctree::
+ :maxdepth: 1
+
+Introduction
+============
+CM Subscriptions are created to subscribe to notifications for CM related changes that happened in the network based on predicates.
+Predicates can be used to filter on CM Handle (id), Datastore and Xpath.
+
+The CM Subscription flow is event driven and adheres to the CNCF Cloud Events Specifications.
+
+Event to create and delete a subscription.
+
+:download:`CM Subscription Event Schema <schemas/ncmp-in-event-schema-1.0.0.json>`
+
+Event to receive status of participants in a subscription.
+
+:download:`CM Subscription Response Event Schema <schemas/ncmp-out-event-schema-1.0.0.json>`
+
+CM Subscriptions Creation
+-------------------------
+To create a subscription, a client sends an event to a configured topic to register its interest with NCMP allowing the client to receive notifications based on the subscription.
+
+CM Subscriptions Deletion
+-------------------------
+If a client no longer wishes to receive notifications based on a registered subscription, the client can delete the subscription by providing the subscription id.
+
+CM Subscriptions Response
+-------------------------
+The response for the involved subscription participants for the Create and Delete flow can be as follows based on how the DMI Plugin responds back to NCMP.
+ - **ACCEPTED:** DMI Plugin successfully applied the subscription.
+ - **REJECTED:** DMI Plugin failed to apply the subscription.
+ - **PENDING:** DMI Plugin failed to respond within a configured time.
+
+**Note.** The Cm Subscription feature relies on the DMI Plugin support for applying the subscriptions. This support is currently not implemented in the ONAP DMI Plugin.
+
+
+
diff --git a/docs/conf.py b/docs/conf.py
index e8bb6630f3..5d7a79941d 100755
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -31,7 +31,7 @@ html_theme = "sphinx_rtd_theme"
html_theme_options = {
"style_nav_header_background": "white",
"sticky_navigation": "False" }
-html_logo = "_static/logo_onap_2017.png"
+html_logo = "_static/logo_onap_2024.png"
html_favicon = "_static/favicon.ico"
html_static_path = ["_static"]
html_show_sphinx = False
diff --git a/docs/cps-delta-endpoints.rst b/docs/cps-delta-endpoints.rst
index b2e4e6041b..47f52dc670 100644
--- a/docs/cps-delta-endpoints.rst
+++ b/docs/cps-delta-endpoints.rst
@@ -1,44 +1,59 @@
.. This work is licensed under a Creative Commons Attribution 4.0 International License.
.. http://creativecommons.org/licenses/by/4.0
-.. Copyright (C) 2021 Pantheon.tech
.. Copyright (C) 2024 TechMahindra Ltd.
.. _cpsDeltaEndpoints:
-.. toctree::
- :maxdepth: 1
-
CPS Delta Endpoints
###################
-The CPS Delta feature provides 1 endpoint:
+The CPS Delta feature provides two endpoints:
+
+1. GET /v2/dataspaces/{dataspace-name}/anchors/{source-anchor-name}/delta
+2. POST /v2/dataspaces/{dataspace-name}/anchors/{source-anchor-name}/delta
-- /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/delta
+Common Parameters of both the endpoints
+---------------------------------------
+Common Path Parameters
+^^^^^^^^^^^^^^^^^^^^^^
+The following parameters are common inputs between the two endpoints:
+ - **dataspace-name:** name of dataspace where the anchor(s) to be used for delta generation are stored.
+ - **source-anchor-name:** the source anchor name, the data under this anchor will be the reference data for delta report generation
-Description
------------
-The following is a Get endpoint, which allows the user to find the delta between configurations stored under two anchors within the same dataspace.
+Common Query Parameter
+^^^^^^^^^^^^^^^^^^^^^^
+Both the endpoint need the following query parameters as input:
+ - **xpath:** the xpath to a particular data node.
+ - Example: /bookstore/categories[@code='1']
-Path Parameters
----------------
-The endpoint takes 2 path parameters as input:
- - **dataspace-name:** name of dataspace where the 2 anchors to be used for delta generation are stored.
- - **anchor-name:** the source anchor name, the data under this anchor will be the reference data for delta report generation
+Description of API 1: Delta between 2 Anchors
+---------------------------------------------
+This API performs a GET operation, which allows the user to find the delta between configurations stored under two anchors within the same dataspace. The API has following input parameters:
-Query Parameters
-----------------
-The endpoint takes 3 query parameters as input:
- - **target-anchor-name:** the data retrieved from target anchor gets compared against the data retrieved from source anchor
- - **xpath:** the xpath to a particular data node, Example: /bookstore/categories[@code='1']
+Specific Query Parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^
+The endpoint takes following additional/specific query parameters as input:
+ - **target-anchor-name:** the data retrieved from target anchor is compared against the data retrieved from source anchor
- **descendants:** specifies the number of descendants to query.
+Description of API 2: Delta between Anchor and JSON payload
+-----------------------------------------------------------
+This API performs a POST operation, which allows the user to find the delta between configurations stored under an anchors and a JSON payload provided as part of the request. The API has the following input parameters:
+
+Request Body for Endpoint 2
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The endpoint accepts a **multipart/form-data** input as part of request body. This allows the user to provide the following inputs as part of request body:
+ - **JSON payload:** this field accepts a valid JSON string as an input. The data provided as part of this JSON will be parsed using the schema, the schema is either retrieved using the anchor name or it can be provided as part of request body using the optional parameter of request body defined below. Once the JSON is parsed and validated, it is compared to the data fetched from the source anchor and the delta report is generated.
+ - **schema-context:** this is an optional parameter and allows the user to provide the schema of the JSON payload, as a yang or zip file, and this schema can be used to parse the JSON string in case the schema of JSON differs from the schema associated with source anchor. If the schema of JSON payload is similar to the schema associated with the anchor then this parameter can be left empty.
+
Sample Delta Report
-------------------
+Both the APIs have the same format for the delta report. The format is as follows:
.. code-block:: json
[
{
- "action": "ADD",
+ "action": "create",
"xpath": "/bookstore/categories/[@code=3]",
"target-data": {
"code": "3,",
@@ -46,7 +61,7 @@ Sample Delta Report
}
},
{
- "action": "REMOVE",
+ "action": "remove",
"xpath": "/bookstore/categories/[@code=1]",
"source-data": {
"code": "1,",
@@ -54,7 +69,7 @@ Sample Delta Report
}
},
{
- "action": "UPDATE",
+ "action": "replace",
"xpath": "/bookstore/categories/[@code=2]",
"source-data": {
"name": "Funny"
diff --git a/docs/cps-delta-feature.rst b/docs/cps-delta-feature.rst
index f3a2f947e4..ee3df45378 100644
--- a/docs/cps-delta-feature.rst
+++ b/docs/cps-delta-feature.rst
@@ -1,6 +1,5 @@
.. This work is licensed under a Creative Commons Attribution 4.0 International License.
.. http://creativecommons.org/licenses/by/4.0
-.. Copyright (C) 2021 Pantheon.tech
.. Copyright (C) 2024 TechMahindra Ltd.
.. _cpsDelta:
@@ -10,24 +9,26 @@
CPS Delta Feature
#################
-- The concept of CPS Delta Feature is to have the ability to find the delta or difference between two configurations stored in CPS DB.
+- The concept of CPS Delta Feature is to have the ability to find the delta or difference between two configurations in CPS.
-- The Delta feature brings a new functionality:
+- The Delta feature provides two new endpoints, providing the following functionalities:
- Ability to find the delta between the configurations stored in two anchors within the same dataspace.
+ - Ability to find the delta between the configurations stored under an anchor and a JSON payload provided as part of REST request.
-The calculated differences can then be used to generate a Delta Report which can be sent over the Kafka Notification Service to the user.
+The difference found are compiled into a Delta Report returned by the server. This report can be used in several different ways, one such use case is sending it over the Kafka Notification Service to the user.
Delta Report Format
-------------------
-The Delta Report is based on the RFC 9144, which defines a set of parameters to be included in the Delta Report. In CPS only the relevant parameters defined in RFC 9144 are used. These include:
- - **action:** This parameter defines how the data nodes changed between two configurations. If data nodes are added, deleted or modified then the 'action' parameter in the delta report will be set to ADD, DELETE or UPDATE respectively for each data node.
+The Delta Report is based on the RFC 6902 and RFC 9144, which define a set of parameters to be included in the Delta Report. In CPS only the relevant parameters defined in the RFCs are used. These include:
+ - **action:** This parameter defines how the data nodes changed between two configurations. If data nodes are added, deleted or modified then the 'action' parameter in the delta report will be set to one of the following pre-defined operation values, i.e., 'create', 'remove' or 'replace' respectively for each data node.
- **xpath:** This parameter will provide the xpath of each data node that has been either added, deleted or modified.
- - **source-data:** this parameter is added to the delta report when a data node is deleted or updated, this represents the source/reference data against which the delta is being generated. In case of newly added data node this field is not included in the delta report.
- - **target-data:** this parameter is added to the delta report when a data node is added or updated, this represents the data values that are being compared to the source data. In case of a data node being deleted this field is not included in the delta report.
+ - **source-data:** this parameter is added to the delta report when a data node is removed or updated, this represents the source/reference data against which the delta is being generated. In case of newly added data node this field is not included in the delta report.
+ - **target-data:** this parameter is added to the delta report when a data node is added or updated, this represents the data values that are being compared to the source data. In case of a data node being removed this field is not included in the delta report.
**Note.** In case of an existing data node being modified, both the source-data and target-data fields are present in the delta report.
+**Additional Information.** `Analysis of RFC 9144 and RFC 6902 for Delta Report generation <https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16523520/Analysis+of+RFC+9144+and+RFC6902+for+Delta+Report+generation>`_
Mechanism for Delta generation
------------------------------
diff --git a/docs/cps-events.rst b/docs/cps-events.rst
index 25a253bada..47aa73f12e 100644
--- a/docs/cps-events.rst
+++ b/docs/cps-events.rst
@@ -13,6 +13,7 @@ CPS Events
cm-handle-lcm-events.rst
data-operation-events.rst
+ cm-notification-subscriptions.rst
.. note::
Legacy async response on a client supplied topic for single cm handle data request are no longer supported. Click link below for the legacy specification.
diff --git a/docs/cps-scheduled-processes.rst b/docs/cps-scheduled-processes.rst
index 032b4b143b..c204e6ca0a 100644
--- a/docs/cps-scheduled-processes.rst
+++ b/docs/cps-scheduled-processes.rst
@@ -22,9 +22,11 @@ Module Sync
-----------
The module sync is a user :ref:`configurable timed process<additional-cps-ncmp-customizations>`,
which is set to search for CM-Handles within CPS with an *'ADVISED'* state.
-Once the CM-Handle(s) is processed by the module sync, the CM-Handle state is then set to *'READY'*, if the process completes successfully.
+Once the CM-Handle is processed by the module sync, the CM-Handle state is then set to *'READY'*, if the process completes successfully.
If for any reason the module sync fails, the CM-Handle state will then be set to *'LOCKED'*,
and the reason for the lock will also be stored within CPS.
+CM-Handles in the *'LOCKED'* state will be retried when the system has availability. CM-Handles in a *'LOCKED'*
+state are processed by the retry mechanism, by setting CM-Handle state back to *'ADVISED'* so the next sync cycle will process those again.
Data Sync
---------
@@ -33,13 +35,3 @@ which is set to search for CM-Handles with a sync state of *'UNSYNCHRONIZED'*.
Once the CM-Handle(s) with a sync state of *'UNSYNCHRONIZED'* is processed by the data sync,
the CM-Handle sync state is then set to *'SYNCHRONIZED'*, if the process completes successfully.
If the data sync fails, the CM-Handle sync state will remain as *'UNSYNCHRONIZED'*, and will be re-attempted.
-
-Retry Mechanism
----------------
-The retry mechanism is a user :ref:`configurable timed process<additional-cps-ncmp-customizations>`,
-which is used to search for CM-Handles which are currently in a *'LOCKED'* state.
-If the CM-Handle is ready to be retried then, the CM-Handle(s) in a *'LOCKED'* state is processed by the retry mechanism,
-the CM-Handle state is then set to *'ADVISED'*.
-Whether the CM-Handle is ready to be retried is dependent on both the number of attempts to sync the CM-Handle,
-and the last update time of the CM-Handle state.
-With each new attempt to unlock the CM-Handle, the time until the CM-Handle can next be retried is doubled.
diff --git a/docs/deployment.rst b/docs/deployment.rst
index ba8fcd9347..b49bc71985 100644
--- a/docs/deployment.rst
+++ b/docs/deployment.rst
@@ -19,6 +19,18 @@ CPS uses PostgreSQL database. As per the `PostgreSQL documentation on resource c
parameter should be set between 25% and 40% of total memory. It has a default value of 128 megabytes, so this should be
set appropriately. For example, given a database with 2GB of memory, 512MB is a recommended value.
+CPS and NCMP Configuration
+==========================
+
+JVM Memory Allocation
+
+Allocating 75% of the container's memory to the JVM heap ensures efficient memory management.
+This helps the JVM make the best use of the allocated resources while leaving enough memory for other processes.
+
+.. code-block:: yaml
+
+ JAVA_TOOL_OPTIONS: "-XX:InitialRAMPercentage=75.0 -XX:MaxRAMPercentage=75.0"
+
CPS OOM Charts
==============
The CPS kubernetes chart is located in the `OOM repository <https://github.com/onap/oom/tree/master/kubernetes/cps>`_.
@@ -284,20 +296,15 @@ Additional CPS-NCMP Customizations
| | | |
| | See also :ref:`cps_common_credentials_retrieval`. | |
+-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.ncmp.timers | Specifies the delay in milliseconds in which the module sync watch dog will wake again after finishing. | ``30000`` |
+| config.ncmp.timers | Specifies the delay in milliseconds in which the module sync watch dog will wake again after finishing. | ``5000`` |
| .advised-modules-sync.sleep-time-ms | | |
| | | |
+-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.ncmp.timers | Specifies the delay in milliseconds in which the retry mechanism watch dog | |
-| .locked-modules-sync.sleep-time-ms | will wake again after finishing. | ``300000`` |
-| | | |
-| | | |
-+-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
| config.ncmp.timers | Specifies the delay in milliseconds in which the data sync watch dog will wake again after finishing. | ``30000`` |
| .cm-handle-data-sync.sleep-time-ms | | |
| | | |
+-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.additional.ncmp.dmi.httpclient | Specifies the maximum time in seconds, to wait for establishing a connection for the HTTP Client. | ``180`` |
+| config.additional.ncmp.dmi.httpclient | Specifies the maximum time in seconds, to wait for establishing a connection for the HTTP Client. | ``30`` |
| .connectionTimeoutInSeconds | | |
+-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
| config.additional.ncmp.dmi.httpclient | Specifies the maximum number of connections allowed per route in the HTTP client. | ``50`` |
@@ -319,32 +326,27 @@ The latest instructions are covered in the `README <https://github.com/onap/cps/
.. Below Label is used by documentation for other CPS components to link here, do not remove even if it gives a warning
.. _cps_common_distributed_datastructures:
-CPS-Core and NCMP Distributed Datastructures
-============================================
+NCMP Distributed Data Structures
+================================
-CPS-Core and NCMP both internally uses embedded distributed datastructure to replicate the state across various instances for low latency.
-These instances require some additional ports to be available. The default range starts from 5701 and based on the number of instances configured they are incremented sequentially.
+NCMP utilizes embedded distributed data structures to replicate state across various instances, ensuring low latency and high performance. Each JVM runs a Hazelcast instance to manage these data structures. To facilitate member visibility and cluster formation, an additional port (defaulting to 5701) must be available.
Below are the list of distributed datastructures that we have.
+--------------+------------------------------------+-----------------------------------------------------------+
-| Component | Datastructure name | Use |
+| Component | Data Structure Name | Use |
+==============+====================================+===========================================================+
-| cps-core | anchorDataCache | Used to resolve prefix for the container name. |
-+--------------+------------------------------------+-----------------------------------------------------------+
| cps-ncmp | moduleSyncStartedOnCmHandles | Watchdog process to register cm handles. |
+--------------+------------------------------------+-----------------------------------------------------------+
| cps-ncmp | dataSyncSemaphores | Watchdog process to sync data from the nodes. |
+--------------+------------------------------------+-----------------------------------------------------------+
| cps-ncmp | moduleSyncWorkQueue | Queue used internally for workers to pick the task. |
+--------------+------------------------------------+-----------------------------------------------------------+
-| cps-ncmp | untrustworthyCmHandlesSet | Stores untrustworthy cm handles whose trust level is NONE.|
+| cps-ncmp | trustLevelPerCmHandle | Stores the trust level per cm handle id |
+--------------+------------------------------------+-----------------------------------------------------------+
| cps-ncmp | trustLevelPerDmiPlugin | Stores the trust level for the dmi-plugins. |
+--------------+------------------------------------+-----------------------------------------------------------+
-| cps-ncmp | moduleSetTagCacheMapConfig | Stores the module set tags for cm handles. |
-+--------------+------------------------------------+-----------------------------------------------------------+
| cps-ncmp | cmNotificationSubscriptionCache | Stores and tracks cm notification subscription requests. |
+--------------+------------------------------------+-----------------------------------------------------------+
-Total number of caches : 8
+Total number of caches : 6
diff --git a/docs/overview.rst b/docs/overview.rst
index 11ed519b64..19ab8b4847 100644
--- a/docs/overview.rst
+++ b/docs/overview.rst
@@ -51,6 +51,6 @@ This is built previously from the CPS-NF-Proxy component.
CPS Project
===========
-* Wiki: `Configuration Persistence Service Project <https://wiki.onap.org/display/DW/Configuration+Persistence+Service+Project>`_
+* Wiki: `Configuration Persistence Service Project <https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16398157/Configuration+Persistence+Service+Project>`_
* Contact Information: onap-discuss@lists.onap.org
-* Meeting details: `Join <https://zoom.us/j/836561560?pwd=TTZNcFhXTWYxMmZ4SlgzcVZZQXluUT09>`_ & `Agenda <https://wiki.onap.org/pages/viewpage.action?pageId=111117075>`_
+* Meeting details: `Join <https://zoom.us/j/836561560?pwd=TTZNcFhXTWYxMmZ4SlgzcVZZQXluUT09>`_ & `Agenda <https://lf-onap.atlassian.net/wiki/spaces/DW/pages/18644995>`_
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index ce2c028076..24ba54fe43 100644
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -16,6 +16,136 @@ CPS Release Notes
.. * * * OSLO * * *
.. ====================
+Version: 3.5.5
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project** | |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images** | onap/cps-and-ncmp:3.5.5 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation** | 3.5.5 Oslo |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release date** | Not yet released |
+| | |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.5.5
+
+Features
+--------
+3.5.5
+
+Version: 3.5.4
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project** | |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images** | onap/cps-and-ncmp:3.5.4 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation** | 3.5.4 Oslo |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release date** | 2024 October 17 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.5.4
+ - `CPS-2403 <https://lf-onap.atlassian.net/browse/CPS-2403>`_ Improve lock handling and queue management during CM-handle Module Sync.
+
+Features
+--------
+3.5.4
+ - `CPS-2408 <https://lf-onap.atlassian.net/browse/CPS-2408>`_ One Hazelcast instance per JVM to manage the distributed data structures.
+
+Version: 3.5.3
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project** | |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images** | onap/cps-and-ncmp:3.5.3 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation** | 3.5.3 Oslo |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release date** | 2024 October 04 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.5.3
+ - `CPS-2353 <https://lf-onap.atlassian.net/browse/CPS-2353>`_ Slow cmHandle registration when we use moduleSetTag, alternateId and dataProducerIdentifier
+ - `CPS-2395 <https://lf-onap.atlassian.net/browse/CPS-2395>`_ Retry mechanism (with back off algorithm) is removed with more frequent watchdog poll
+ - `CPS-2409 <https://lf-onap.atlassian.net/browse/CPS-2409>`_ Return NONE for get effective trust level api if the trust level caches empty (restart case)
+ - `CPS-2430 <https://lf-onap.atlassian.net/browse/CPS-2430>`_ Fix memory leak related to using arrays in Hibernate
+
+
+Features
+--------
+3.5.3
+ - `CPS-2247 <https://lf-onap.atlassian.net/browse/CPS-2247>`_ Policy Executor: Invoke Policy Executor and handle 'deny' response
+ - `CPS-2412 <https://lf-onap.atlassian.net/browse/CPS-2412>`_ Policy Executor: handle errors
+ - `CPS-2417 <https://lf-onap.atlassian.net/browse/CPS-2417>`_ Remove Hazelcast cache for prefix resolver
+
+
+Version: 3.5.2
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project** | |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images** | onap/cps-and-ncmp:3.5.2 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation** | 3.5.2 Oslo |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release date** | 2024 August 21 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.5.2
+ - `CPS-2306 <https://lf-onap.atlassian.net/browse/CPS-2306>`_ Update response message for data validation failure and make it consistent across APIs
+ - `CPS-2319 <https://lf-onap.atlassian.net/browse/CPS-2319>`_ Fix "Create a node" and "Add List Elements" APIs response code
+ - `CPS-2372 <https://lf-onap.atlassian.net/browse/CPS-2372>`_ Blank alternate ID overwrites existing one
+
+Features
+--------
+3.5.2
+ - `CPS-1812 <https://lf-onap.atlassian.net/browse/CPS-1812>`_ CM Data Subscriptions ( Create, Delete and Merging ) with positive scenarios
+ - `CPS-2326 <https://lf-onap.atlassian.net/browse/CPS-2326>`_ Uplift liquibase-core dependency to 4.28.0
+ - `CPS-2353 <https://lf-onap.atlassian.net/browse/CPS-2353>`_ Improve registration performance with moduleSetTag
+ - `CPS-2366 <https://lf-onap.atlassian.net/browse/CPS-2366>`_ Improve registration performance with use of alternateID
+
Version: 3.5.1
==============
@@ -32,19 +162,20 @@ Release Data
| **Release designation** | 3.5.1 Oslo |
| | |
+--------------------------------------+--------------------------------------------------------+
-| **Release date** | Not yet released |
+| **Release date** | 2024 July 15 |
| | |
+--------------------------------------+--------------------------------------------------------+
Bug Fixes
---------
3.5.1
- - `CPS-2302 <https://jira.onap.org/browse/CPS-2302>`_ Fix handling of special characters in moduleSetTag.
+ - `CPS-2302 <https://lf-onap.atlassian.net/browse/CPS-2302>`_ Fix handling of special characters in moduleSetTag.
Features
--------
3.5.1
- - `CPS-2121 <https://jira.onap.org/browse/CPS-2121>`_ Enabled http client prometheus metrics and manage high cardinality using URL template.
+ - `CPS-2121 <https://lf-onap.atlassian.net/browse/CPS-2121>`_ Enabled http client prometheus metrics and manage high cardinality using URL template.
+ - `CPS-2289 <https://lf-onap.atlassian.net/browse/CPS-2289>`_ Support for CPS Path Query in NCMP Inventory Cm Handle Search.
Version: 3.5.0
==============
@@ -73,8 +204,8 @@ Bug Fixes
Features
--------
3.5.0
- - `CPS-989 <https://jira.onap.org/browse/CPS-989>`_ Replace RestTemplate with WebClient.
- - `CPS-2172 <https://jira.onap.org/browse/CPS-2172>`_ Support for OpenTelemetry Tracing.
+ - `CPS-989 <https://lf-onap.atlassian.net/browse/CPS-989>`_ Replace RestTemplate with WebClient.
+ - `CPS-2172 <https://lf-onap.atlassian.net/browse/CPS-2172>`_ Support for OpenTelemetry Tracing.
.. =========================
.. * * * NEW DELHI * * *
@@ -103,12 +234,12 @@ Release Data
Bug Fixes
---------
3.4.9
- - `CPS-2211 <https://jira.onap.org/browse/CPS-2211>`_ Toggle switch to disable CPS Core change events if not used by application. Set CPS_CHANGE_EVENT_NOTIFICATIONS_ENABLED environment variable for the same.
+ - `CPS-2211 <https://lf-onap.atlassian.net/browse/CPS-2211>`_ Toggle switch to disable CPS Core change events if not used by application. Set CPS_CHANGE_EVENT_NOTIFICATIONS_ENABLED environment variable for the same.
Features
--------
3.4.9
- - `CPS-1836 <https://jira.onap.org/browse/CPS-1836>`_ Delta between anchor and JSON payload.
+ - `CPS-1836 <https://lf-onap.atlassian.net/browse/CPS-1836>`_ Delta between anchor and JSON payload.
Version: 3.4.8
==============
@@ -133,10 +264,10 @@ Release Data
Bug Fixes
---------
3.4.8
- - `CPS-2186 <https://jira.onap.org/browse/CPS-2186>`_ Report async task failures to client topic during data operations request
- - `CPS-2190 <https://jira.onap.org/browse/CPS-2190>`_ Improve performance of NCMP module searches
- - `CPS-2194 <https://jira.onap.org/browse/CPS-2194>`_ Added defaults for CPS and DMI username and password
- - `CPS-2204 <https://jira.onap.org/browse/CPS-2204>`_ Added error handling for yang module upgrade operation
+ - `CPS-2186 <https://lf-onap.atlassian.net/browse/CPS-2186>`_ Report async task failures to client topic during data operations request
+ - `CPS-2190 <https://lf-onap.atlassian.net/browse/CPS-2190>`_ Improve performance of NCMP module searches
+ - `CPS-2194 <https://lf-onap.atlassian.net/browse/CPS-2194>`_ Added defaults for CPS and DMI username and password
+ - `CPS-2204 <https://lf-onap.atlassian.net/browse/CPS-2204>`_ Added error handling for yang module upgrade operation
Features
--------
@@ -164,13 +295,13 @@ Release Data
Bug Fixes
---------
3.4.7
- - `CPS-2150 <https://jira.onap.org/browse/CPS-2150>`_ Fix for Async task execution failed by TimeoutException.
+ - `CPS-2150 <https://lf-onap.atlassian.net/browse/CPS-2150>`_ Fix for Async task execution failed by TimeoutException.
Features
--------
3.4.7
- - `CPS-2061 <https://jira.onap.org/browse/CPS-2061>`_ Liquibase Steps Condensing and Cleanup.
- - `CPS-2101 <https://jira.onap.org/browse/CPS-2101>`_ Uplift Spring Boot to 3.2.4 version.
+ - `CPS-2061 <https://lf-onap.atlassian.net/browse/CPS-2061>`_ Liquibase Steps Condensing and Cleanup.
+ - `CPS-2101 <https://lf-onap.atlassian.net/browse/CPS-2101>`_ Uplift Spring Boot to 3.2.4 version.
Version: 3.4.6
==============
@@ -195,12 +326,12 @@ Release Data
Bug Fixes
---------
3.4.6
- - `CPS-2126 <https://jira.onap.org/browse/CPS-2126>`_ Passing HTTP Authorization Bearer Token to DMI Plugins.
+ - `CPS-2126 <https://lf-onap.atlassian.net/browse/CPS-2126>`_ Passing HTTP Authorization Bearer Token to DMI Plugins.
Features
--------
- - `CPS-2133 <https://jira.onap.org/browse/CPS-2133>`_ Revert Uplift of Spring Boot version from 3.2.2 to 3.1.2
+ - `CPS-2133 <https://lf-onap.atlassian.net/browse/CPS-2133>`_ Revert Uplift of Spring Boot version from 3.2.2 to 3.1.2
Notes
-----
@@ -237,7 +368,7 @@ Bug Fixes
Features
--------
- - `CPS-2101 <https://jira.onap.org/browse/CPS-2101>`_ Uplift Spring Boot version to 3.2.2
+ - `CPS-2101 <https://lf-onap.atlassian.net/browse/CPS-2101>`_ Uplift Spring Boot version to 3.2.2
Version: 3.4.4
@@ -263,12 +394,12 @@ Release Data
Bug Fixes
---------
3.4.4
- - `CPS-2027 <https://jira.onap.org/browse/CPS-2027>`_ Upgrade Yang modules using module set tag functionalities fix
+ - `CPS-2027 <https://lf-onap.atlassian.net/browse/CPS-2027>`_ Upgrade Yang modules using module set tag functionalities fix
Features
--------
- - `CPS-2057 <https://jira.onap.org/browse/CPS-2057>`_ Leaf lists are sorted by default if Yang model does not specify order.
- - `CPS-2087 <https://jira.onap.org/browse/CPS-2087>`_ Performance improvement of CPS Path Queries.
+ - `CPS-2057 <https://lf-onap.atlassian.net/browse/CPS-2057>`_ Leaf lists are sorted by default if Yang model does not specify order.
+ - `CPS-2087 <https://lf-onap.atlassian.net/browse/CPS-2087>`_ Performance improvement of CPS Path Queries.
Version: 3.4.3
@@ -294,20 +425,20 @@ Release Data
Bug Fixes
---------
3.4.3
- - `CPS-2000 <https://jira.onap.org/browse/CPS-2000>`_ Fix for Schema object cache not being distributed.
- - `CPS-2027 <https://jira.onap.org/browse/CPS-2027>`_ Fixes for upgrade yang modules using module set tag.
- - `CPS-2070 <https://jira.onap.org/browse/CPS-2070>`_ Add retry interval for Kafka consumer.
+ - `CPS-2000 <https://lf-onap.atlassian.net/browse/CPS-2000>`_ Fix for Schema object cache not being distributed.
+ - `CPS-2027 <https://lf-onap.atlassian.net/browse/CPS-2027>`_ Fixes for upgrade yang modules using module set tag.
+ - `CPS-2070 <https://lf-onap.atlassian.net/browse/CPS-2070>`_ Add retry interval for Kafka consumer.
Features
--------
- - `CPS-1824 <https://jira.onap.org/browse/CPS-1824>`_ CPS Delta between 2 anchors.
- - `CPS-2072 <https://jira.onap.org/browse/CPS-2072>`_ Add maven classifier to Spring Boot JAR.
- - `CPS-1135 <https://jira.onap.org/browse/CPS-1135>`_ Extend CPS Module API to allow retrieval single module definition.
+ - `CPS-1824 <https://lf-onap.atlassian.net/browse/CPS-1824>`_ CPS Delta between 2 anchors.
+ - `CPS-2072 <https://lf-onap.atlassian.net/browse/CPS-2072>`_ Add maven classifier to Spring Boot JAR.
+ - `CPS-1135 <https://lf-onap.atlassian.net/browse/CPS-1135>`_ Extend CPS Module API to allow retrieval single module definition.
Notes
-----
The maven build of cps-application has been changed so that the JAR produced by spring-boot-maven-plugin has a
-*-springboot* classifier (`CPS-2072 <https://jira.onap.org/browse/CPS-2072>`_). This means that the filename
+*-springboot* classifier (`CPS-2072 <https://lf-onap.atlassian.net/browse/CPS-2072>`_). This means that the filename
of the Spring Boot JAR is *cps-application-3.4.3-springboot.jar*.
Version: 3.4.2
@@ -337,10 +468,10 @@ Bug Fixes
Features
--------
- - `CPS-1638 <https://jira.onap.org/browse/CPS-1638>`_ Introduce trust level for CM handle.
- - `CPS-1795 <https://jira.onap.org/browse/CPS-1795>`_ Double performance of CPS write operations (via write batching)
- - `CPS-2018 <https://jira.onap.org/browse/CPS-2018>`_ Improve performance of CPS update operations.
- - `CPS-2019 <https://jira.onap.org/browse/CPS-2019>`_ Improve performance of saving CM handles.
+ - `CPS-1638 <https://lf-onap.atlassian.net/browse/CPS-1638>`_ Introduce trust level for CM handle.
+ - `CPS-1795 <https://lf-onap.atlassian.net/browse/CPS-1795>`_ Double performance of CPS write operations (via write batching)
+ - `CPS-2018 <https://lf-onap.atlassian.net/browse/CPS-2018>`_ Improve performance of CPS update operations.
+ - `CPS-2019 <https://lf-onap.atlassian.net/browse/CPS-2019>`_ Improve performance of saving CM handles.
Notes
-----
@@ -352,7 +483,7 @@ Known Limitations, Issues and Workarounds
*System Limitations*
For upgrading, CPS uses Liquibase for database upgrades. In order to enable Hibernate write batching
-(`CPS-1795 <https://jira.onap.org/browse/CPS-1795>`_), a change to the database entity ID generation is required.
+(`CPS-1795 <https://lf-onap.atlassian.net/browse/CPS-1795>`_), a change to the database entity ID generation is required.
As such, *this release does not fully support In-Service Software Upgrade* - CPS will not store new DataNodes and
NCMP will not register new CM-handles during an upgrade with old and new versions of CPS running concurrently.
Other operations (read, update, delete) are not impacted.
@@ -381,19 +512,19 @@ Release Data
Bug Fixes
---------
3.4.1
- - `CPS-1979 <https://jira.onap.org/browse/CPS-1979>`_ Bug fix for Invalid topic name suffix.
+ - `CPS-1979 <https://lf-onap.atlassian.net/browse/CPS-1979>`_ Bug fix for Invalid topic name suffix.
Features
--------
- CPS-Temporal is no longer supported and any related documentation has been removed.
- - `CPS-1733 <https://jira.onap.org/browse/CPS-1733>`_ Upgrade YANG schema-set for CM handle without removing and adding it.
- - `CPS-1980 <https://jira.onap.org/browse/CPS-1980>`_ Exposing health and cluster metrics for hazelcast.
- - `CPS-1994 <https://jira.onap.org/browse/CPS-1994>`_ Use Apache Http Client for DMI REST requests.
- - `CPS-2005 <https://jira.onap.org/browse/CPS-2005>`_ Removing notification feature for cps updated events ( exclusively used by cps-temporal )
+ - `CPS-1733 <https://lf-onap.atlassian.net/browse/CPS-1733>`_ Upgrade YANG schema-set for CM handle without removing and adding it.
+ - `CPS-1980 <https://lf-onap.atlassian.net/browse/CPS-1980>`_ Exposing health and cluster metrics for hazelcast.
+ - `CPS-1994 <https://lf-onap.atlassian.net/browse/CPS-1994>`_ Use Apache Http Client for DMI REST requests.
+ - `CPS-2005 <https://lf-onap.atlassian.net/browse/CPS-2005>`_ Removing notification feature for cps updated events ( exclusively used by cps-temporal )
Known Issues
------------
- - `CPS-2000 <https://jira.onap.org/browse/CPS-2000>`_ Schema object cache is not distributed.
+ - `CPS-2000 <https://lf-onap.atlassian.net/browse/CPS-2000>`_ Schema object cache is not distributed.
Version: 3.4.0
@@ -419,7 +550,7 @@ Release Data
Bug Fixes
---------
3.4.0
- - `CPS-1956 <https://jira.onap.org/browse/CPS-1956>`_ Bug fix for No yang resources stored during cmhandle discovery.
+ - `CPS-1956 <https://lf-onap.atlassian.net/browse/CPS-1956>`_ Bug fix for No yang resources stored during cmhandle discovery.
.. ========================
.. * * * MONTREAL * * *
@@ -448,8 +579,8 @@ Release Data
Bug Fixes
---------
3.3.9
- - `CPS-1923 <https://jira.onap.org/browse/CPS-1923>`_ CPS and NCMP changed management endpoint and port from /manage to /actuator and port same as cps application port.
- - `CPS-1933 <https://jira.onap.org/browse/CPS-1933>`_ Setting up the class loader explicitly in hazelcast config.
+ - `CPS-1923 <https://lf-onap.atlassian.net/browse/CPS-1923>`_ CPS and NCMP changed management endpoint and port from /manage to /actuator and port same as cps application port.
+ - `CPS-1933 <https://lf-onap.atlassian.net/browse/CPS-1933>`_ Setting up the class loader explicitly in hazelcast config.
Features
--------
@@ -480,7 +611,7 @@ Bug Fixes
Features
--------
- - `CPS-1888 <https://jira.onap.org/browse/CPS-1888>`_ Uplift Spring Boot to 3.1.2.
+ - `CPS-1888 <https://lf-onap.atlassian.net/browse/CPS-1888>`_ Uplift Spring Boot to 3.1.2.
Version: 3.3.7
==============
@@ -505,11 +636,11 @@ Release Data
Bug Fixes
---------
3.3.7
- - `CPS-1866 <https://jira.onap.org/browse/CPS-1866>`_ Fix ClassDefNotFoundError in opendaylight Yang parser
+ - `CPS-1866 <https://lf-onap.atlassian.net/browse/CPS-1866>`_ Fix ClassDefNotFoundError in opendaylight Yang parser
Features
--------
- - `CPS-1789 <https://jira.onap.org/browse/CPS-1789>`_ CPS Upgrade to Springboot 3.0.
+ - `CPS-1789 <https://lf-onap.atlassian.net/browse/CPS-1789>`_ CPS Upgrade to Springboot 3.0.
Note
----
@@ -538,13 +669,13 @@ Release Data
Bug Fixes
---------
3.3.6
- - `CPS-1841 <https://jira.onap.org/browse/CPS-1841>`_ Update of top-level data node fails with exception
- - `CPS-1842 <https://jira.onap.org/browse/CPS-1842>`_ Replace event-id with correlation-id for data read operation cloud event
+ - `CPS-1841 <https://lf-onap.atlassian.net/browse/CPS-1841>`_ Update of top-level data node fails with exception
+ - `CPS-1842 <https://lf-onap.atlassian.net/browse/CPS-1842>`_ Replace event-id with correlation-id for data read operation cloud event
Features
--------
- - `CPS-1696 <https://jira.onap.org/browse/CPS-1696>`_ Get Data Node to return entire List data node.
- - `CPS-1819 <https://jira.onap.org/browse/CPS-1819>`_ Ability to disable sending authorization header.
+ - `CPS-1696 <https://lf-onap.atlassian.net/browse/CPS-1696>`_ Get Data Node to return entire List data node.
+ - `CPS-1819 <https://lf-onap.atlassian.net/browse/CPS-1819>`_ Ability to disable sending authorization header.
Version: 3.3.5
@@ -573,7 +704,7 @@ Bug Fixes
Features
--------
- - `CPS-1760 <https://jira.onap.org/browse/CPS-1760>`_ Improve handling of special characters in Cps Paths
+ - `CPS-1760 <https://lf-onap.atlassian.net/browse/CPS-1760>`_ Improve handling of special characters in Cps Paths
Version: 3.3.4
==============
@@ -601,7 +732,7 @@ Bug Fixes
Features
--------
- - `CPS-1767 <https://jira.onap.org/browse/CPS-1767>`_ Upgrade CPS to java 17
+ - `CPS-1767 <https://lf-onap.atlassian.net/browse/CPS-1767>`_ Upgrade CPS to java 17
Version: 3.3.3
==============
@@ -629,9 +760,9 @@ Bug Fixes
Features
--------
- - `CPS-1515 <https://jira.onap.org/browse/CPS-1515>`_ Support Multiple CM-Handles for NCMP Get Operation
- - `CPS-1675 <https://jira.onap.org/browse/CPS-1675>`_ Persistence write performance improvement(s)
- - `CPS-1745 <https://jira.onap.org/browse/CPS-1745>`_ Upgrade to Openapi 3.0.3
+ - `CPS-1515 <https://lf-onap.atlassian.net/browse/CPS-1515>`_ Support Multiple CM-Handles for NCMP Get Operation
+ - `CPS-1675 <https://lf-onap.atlassian.net/browse/CPS-1675>`_ Persistence write performance improvement(s)
+ - `CPS-1745 <https://lf-onap.atlassian.net/browse/CPS-1745>`_ Upgrade to Openapi 3.0.3
Version: 3.3.2
==============
@@ -656,15 +787,15 @@ Release Data
Bug Fixes
---------
3.3.2
- - `CPS-1716 <https://jira.onap.org/browse/CPS-1716>`_ NCMP: Java Heap OutOfMemory errors and slow registration in case of 20k cmhandles
+ - `CPS-1716 <https://lf-onap.atlassian.net/browse/CPS-1716>`_ NCMP: Java Heap OutOfMemory errors and slow registration in case of 20k cmhandles
Features
--------
- - `CPS-1006 <https://jira.onap.org/browse/CPS-1006>`_ Extend CPS PATCH API to allow update of leaves for multiple data nodes
- - `CPS-1273 <https://jira.onap.org/browse/CPS-1273>`_ Add <,> operators support to cps-path
- - `CPS-1664 <https://jira.onap.org/browse/CPS-1664>`_ Use recursive SQL to fetch descendants in CpsPath queries to improve query performance
- - `CPS-1676 <https://jira.onap.org/browse/CPS-1676>`_ Entity ID types do not match types in database definition
- - `CPS-1677 <https://jira.onap.org/browse/CPS-1677>`_ Remove dataspace_id column from Fragment table
+ - `CPS-1006 <https://lf-onap.atlassian.net/browse/CPS-1006>`_ Extend CPS PATCH API to allow update of leaves for multiple data nodes
+ - `CPS-1273 <https://lf-onap.atlassian.net/browse/CPS-1273>`_ Add <,> operators support to cps-path
+ - `CPS-1664 <https://lf-onap.atlassian.net/browse/CPS-1664>`_ Use recursive SQL to fetch descendants in CpsPath queries to improve query performance
+ - `CPS-1676 <https://lf-onap.atlassian.net/browse/CPS-1676>`_ Entity ID types do not match types in database definition
+ - `CPS-1677 <https://lf-onap.atlassian.net/browse/CPS-1677>`_ Remove dataspace_id column from Fragment table
Version: 3.3.1
==============
@@ -693,11 +824,11 @@ Bug Fixes
Features
--------
- - `CPS-1272 <https://jira.onap.org/browse/CPS-1272>`_ Add Contains operation to CPS Path
- - `CPS-1573 <https://jira.onap.org/browse/CPS-1573>`_ Remove 32K limit for DB operations
- - `CPS-1627 <https://jira.onap.org/browse/CPS-1627>`_ Dependency versions uplift because of vulnerability issues
- - `CPS-1629 <https://jira.onap.org/browse/CPS-1629>`_ Ordering of leaf elements to support combination of AND/OR in cps-path
- - `CPS-1637 <https://jira.onap.org/browse/CPS-1637>`_ Extend hazelcast to work on kubernetes
+ - `CPS-1272 <https://lf-onap.atlassian.net/browse/CPS-1272>`_ Add Contains operation to CPS Path
+ - `CPS-1573 <https://lf-onap.atlassian.net/browse/CPS-1573>`_ Remove 32K limit for DB operations
+ - `CPS-1627 <https://lf-onap.atlassian.net/browse/CPS-1627>`_ Dependency versions uplift because of vulnerability issues
+ - `CPS-1629 <https://lf-onap.atlassian.net/browse/CPS-1629>`_ Ordering of leaf elements to support combination of AND/OR in cps-path
+ - `CPS-1637 <https://lf-onap.atlassian.net/browse/CPS-1637>`_ Extend hazelcast to work on kubernetes
Version: 3.3.0
==============
@@ -726,8 +857,8 @@ Bug Fixes
Features
--------
- - `CPS-1215 <https://jira.onap.org/browse/CPS-1215>`_ Add OR operation for CPS Path
- - `CPS-1617 <https://jira.onap.org/browse/CPS-1617>`_ Use cascade delete in fragments table
+ - `CPS-1215 <https://lf-onap.atlassian.net/browse/CPS-1215>`_ Add OR operation for CPS Path
+ - `CPS-1617 <https://lf-onap.atlassian.net/browse/CPS-1617>`_ Use cascade delete in fragments table
.. ======================
.. * * * LONDON * * *
@@ -756,12 +887,12 @@ Release Data
Bug Fixes
---------
3.2.6
- - `CPS-1526 <https://jira.onap.org/browse/CPS-1526>`_ Fix response message for PATCH operation
- - `CPS-1563 <https://jira.onap.org/browse/CPS-1563>`_ Fix 500 response error on id-searches with empty parameters
+ - `CPS-1526 <https://lf-onap.atlassian.net/browse/CPS-1526>`_ Fix response message for PATCH operation
+ - `CPS-1563 <https://lf-onap.atlassian.net/browse/CPS-1563>`_ Fix 500 response error on id-searches with empty parameters
Features
--------
- - `CPS-1396 <https://jira.onap.org/browse/CPS-1396>`_ Query data nodes across all anchors under one dataspace
+ - `CPS-1396 <https://lf-onap.atlassian.net/browse/CPS-1396>`_ Query data nodes across all anchors under one dataspace
Version: 3.2.5
==============
@@ -786,7 +917,7 @@ Release Data
Bug Fixes
---------
3.2.5
- - `CPS-1537 <https://jira.onap.org/browse/CPS-1537>`_ Introduce control switch for model loader functionality.
+ - `CPS-1537 <https://lf-onap.atlassian.net/browse/CPS-1537>`_ Introduce control switch for model loader functionality.
Features
--------
@@ -815,8 +946,8 @@ Release Data
Bug Fixes
---------
3.2.4
- - `CPS-1533 <https://jira.onap.org/browse/CPS-1533>`_ Fix for Temp tables cause Out of shared memory errors in Postgres
- - `CPS-1537 <https://jira.onap.org/browse/CPS-1537>`_ NCMP failed to start due to issue in SubscriptionModelLoader
+ - `CPS-1533 <https://lf-onap.atlassian.net/browse/CPS-1533>`_ Fix for Temp tables cause Out of shared memory errors in Postgres
+ - `CPS-1537 <https://lf-onap.atlassian.net/browse/CPS-1537>`_ NCMP failed to start due to issue in SubscriptionModelLoader
Features
--------
@@ -845,12 +976,12 @@ Release Data
Bug Fixes
---------
3.2.3
- - `CPS-1494 <https://jira.onap.org/browse/CPS-1494>`_ NCMP Inventory Performance Improvements
+ - `CPS-1494 <https://lf-onap.atlassian.net/browse/CPS-1494>`_ NCMP Inventory Performance Improvements
Features
--------
- - `CPS-1401 <https://jira.onap.org/browse/CPS-1401>`_ Added V2 of Get Data Node API,support to retrieve all data nodes under an anchor
- - `CPS-1502 <https://jira.onap.org/browse/CPS-1502>`_ Delete Performance Improvements
+ - `CPS-1401 <https://lf-onap.atlassian.net/browse/CPS-1401>`_ Added V2 of Get Data Node API,support to retrieve all data nodes under an anchor
+ - `CPS-1502 <https://lf-onap.atlassian.net/browse/CPS-1502>`_ Delete Performance Improvements
Version: 3.2.2
==============
@@ -875,7 +1006,7 @@ Release Data
Bug Fixes
---------
3.2.2
- - `CPS-1173 <https://jira.onap.org/browse/CPS-1173>`_ Delete Performance Improvements.
+ - `CPS-1173 <https://lf-onap.atlassian.net/browse/CPS-1173>`_ Delete Performance Improvements.
Features
--------
@@ -904,40 +1035,40 @@ Release Data
Features
--------
3.2.1
- - `CPS-341 <https://jira.onap.org/browse/CPS-341>`_ Added support for multiple data tree instances under 1 anchor.
- - `CPS-1002 <https://jira.onap.org/browse/CPS-1002>`_ Add CPS-E-05 endpoint for 'Query data, NCMP-Operational Datastore' using cpsPaths
- - `CPS-1182 <https://jira.onap.org/browse/CPS-1182>`_ Upgrade Opendaylight
- - `CPS-1185 <https://jira.onap.org/browse/CPS-1185>`_ Get all dataspaces.
- - `CPS-1186 <https://jira.onap.org/browse/CPS-1186>`_ Get single dataspace.
- - `CPS-1187 <https://jira.onap.org/browse/CPS-1187>`_ Added API to get all schema sets for a given dataspace.
- - `CPS-1236 <https://jira.onap.org/browse/CPS-1236>`_ DMI audit support for NCMP: Filter on any properties of CM Handles
- - `CPS-1257 <https://jira.onap.org/browse/CPS-1257>`_ Added support for application/xml Content-Type (write only).
- - `CPS-1381 <https://jira.onap.org/browse/CPS-1381>`_ Query large outputs using limit/depth/pagination
- - `CPS-1421 <https://jira.onap.org/browse/CPS-1421>`_ Optimized query for large number of hits with descendants.
- - `CPS-1422 <https://jira.onap.org/browse/CPS-1422>`_ Fetch CM handles by collection of xpaths (CPS Core)
- - `CPS-1424 <https://jira.onap.org/browse/CPS-1424>`_ Updating CmHandleStates using batch operation
- - `CPS-1439 <https://jira.onap.org/browse/CPS-1439>`_ Use native query to delete data nodes
+ - `CPS-341 <https://lf-onap.atlassian.net/browse/CPS-341>`_ Added support for multiple data tree instances under 1 anchor.
+ - `CPS-1002 <https://lf-onap.atlassian.net/browse/CPS-1002>`_ Add CPS-E-05 endpoint for 'Query data, NCMP-Operational Datastore' using cpsPaths
+ - `CPS-1182 <https://lf-onap.atlassian.net/browse/CPS-1182>`_ Upgrade Opendaylight
+ - `CPS-1185 <https://lf-onap.atlassian.net/browse/CPS-1185>`_ Get all dataspaces.
+ - `CPS-1186 <https://lf-onap.atlassian.net/browse/CPS-1186>`_ Get single dataspace.
+ - `CPS-1187 <https://lf-onap.atlassian.net/browse/CPS-1187>`_ Added API to get all schema sets for a given dataspace.
+ - `CPS-1236 <https://lf-onap.atlassian.net/browse/CPS-1236>`_ DMI audit support for NCMP: Filter on any properties of CM Handles
+ - `CPS-1257 <https://lf-onap.atlassian.net/browse/CPS-1257>`_ Added support for application/xml Content-Type (write only).
+ - `CPS-1381 <https://lf-onap.atlassian.net/browse/CPS-1381>`_ Query large outputs using limit/depth/pagination
+ - `CPS-1421 <https://lf-onap.atlassian.net/browse/CPS-1421>`_ Optimized query for large number of hits with descendants.
+ - `CPS-1422 <https://lf-onap.atlassian.net/browse/CPS-1422>`_ Fetch CM handles by collection of xpaths (CPS Core)
+ - `CPS-1424 <https://lf-onap.atlassian.net/browse/CPS-1424>`_ Updating CmHandleStates using batch operation
+ - `CPS-1439 <https://lf-onap.atlassian.net/browse/CPS-1439>`_ Use native query to delete data nodes
Bug Fixes
---------
3.2.1
- - `CPS-1171 <https://jira.onap.org/browse/CPS-1171>`_ Optimized retrieval of data nodes with many descendants.
- - `CPS-1288 <https://jira.onap.org/browse/CPS-1288>`_ Hazelcast TTL for IMap is not working
- - `CPS-1289 <https://jira.onap.org/browse/CPS-1289>`_ Getting wrong error code for create node api
- - `CPS-1326 <https://jira.onap.org/browse/CPS-1326>`_ Creation of DataNodeBuilder with module name prefix is very slow
- - `CPS-1344 <https://jira.onap.org/browse/CPS-1344>`_ Top level container (prefix) is not always the first module
- - `CPS-1350 <https://jira.onap.org/browse/CPS-1350>`_ Add Basic Authentication to CPS/NCMP OpenAPI Definitions.
- - `CPS-1352 <https://jira.onap.org/browse/CPS-1352>`_ Handle YangChoiceNode in right format.
- - `CPS-1409 <https://jira.onap.org/browse/CPS-1409>`_ Fix Delete uses case with '/' in path.
- - `CPS-1433 <https://jira.onap.org/browse/CPS-1433>`_ Fix to allow posting data with '/' key fields.
- - `CPS-1442 <https://jira.onap.org/browse/CPS-1442>`_ CPS PATCH operation does not merge existing data
- - `CPS-1446 <https://jira.onap.org/browse/CPS-1446>`_ Locked cmhandles and ready to locked state transitions causing long cmHandle discovery
- - `CPS-1457 <https://jira.onap.org/browse/CPS-1457>`_ CpsDataPersistenceService#getDataNodes uses non-normalized xpaths
- - `CPS-1458 <https://jira.onap.org/browse/CPS-1458>`_ CpsDataPersistenceService#getDataNodes does not handle root xpath
- - `CPS-1460 <https://jira.onap.org/browse/CPS-1460>`_ CPS Path Processing Performance Test duration is too low
+ - `CPS-1171 <https://lf-onap.atlassian.net/browse/CPS-1171>`_ Optimized retrieval of data nodes with many descendants.
+ - `CPS-1288 <https://lf-onap.atlassian.net/browse/CPS-1288>`_ Hazelcast TTL for IMap is not working
+ - `CPS-1289 <https://lf-onap.atlassian.net/browse/CPS-1289>`_ Getting wrong error code for create node api
+ - `CPS-1326 <https://lf-onap.atlassian.net/browse/CPS-1326>`_ Creation of DataNodeBuilder with module name prefix is very slow
+ - `CPS-1344 <https://lf-onap.atlassian.net/browse/CPS-1344>`_ Top level container (prefix) is not always the first module
+ - `CPS-1350 <https://lf-onap.atlassian.net/browse/CPS-1350>`_ Add Basic Authentication to CPS/NCMP OpenAPI Definitions.
+ - `CPS-1352 <https://lf-onap.atlassian.net/browse/CPS-1352>`_ Handle YangChoiceNode in right format.
+ - `CPS-1409 <https://lf-onap.atlassian.net/browse/CPS-1409>`_ Fix Delete uses case with '/' in path.
+ - `CPS-1433 <https://lf-onap.atlassian.net/browse/CPS-1433>`_ Fix to allow posting data with '/' key fields.
+ - `CPS-1442 <https://lf-onap.atlassian.net/browse/CPS-1442>`_ CPS PATCH operation does not merge existing data
+ - `CPS-1446 <https://lf-onap.atlassian.net/browse/CPS-1446>`_ Locked cmhandles and ready to locked state transitions causing long cmHandle discovery
+ - `CPS-1457 <https://lf-onap.atlassian.net/browse/CPS-1457>`_ CpsDataPersistenceService#getDataNodes uses non-normalized xpaths
+ - `CPS-1458 <https://lf-onap.atlassian.net/browse/CPS-1458>`_ CpsDataPersistenceService#getDataNodes does not handle root xpath
+ - `CPS-1460 <https://lf-onap.atlassian.net/browse/CPS-1460>`_ CPS Path Processing Performance Test duration is too low
3.2.0
- - `CPS-1312 <https://jira.onap.org/browse/CPS-1312>`_ CPS(/NCMP) does not have version control.
+ - `CPS-1312 <https://lf-onap.atlassian.net/browse/CPS-1312>`_ CPS(/NCMP) does not have version control.
Known Limitations, Issues and Workarounds
-----------------------------------------
@@ -976,8 +1107,8 @@ Release Data
Bug Fixes
---------
- - `CPS-1265 <https://jira.onap.org/browse/CPS-1265>`_ Revision field should not be required (NotNull) on cps-ri YangResourceEntity
- - `CPS-1294 <https://jira.onap.org/browse/CPS-1294>`_ Kafka communication fault caused cmHandle registration error
+ - `CPS-1265 <https://lf-onap.atlassian.net/browse/CPS-1265>`_ Revision field should not be required (NotNull) on cps-ri YangResourceEntity
+ - `CPS-1294 <https://lf-onap.atlassian.net/browse/CPS-1294>`_ Kafka communication fault caused cmHandle registration error
Version: 3.1.3
==============
@@ -1056,7 +1187,7 @@ Security Notes
*Fixed Security Issues*
- - `CPS-1226 <https://jira.onap.org/browse/CPS-1226>`_ Security bug in the logs
+ - `CPS-1226 <https://lf-onap.atlassian.net/browse/CPS-1226>`_ Security bug in the logs
Version: 3.1.0
==============
@@ -1080,48 +1211,48 @@ Release Data
Features
--------
- - `CPS-340 <https://jira.onap.org/browse/CPS-340>`_ Patch and update the root data node
- - `CPS-575 <https://jira.onap.org/browse/CPS-575>`_ Write data for cmHandle using ncmp-datastores:passthrough-running (NCMP.)
- - `CPS-731 <https://jira.onap.org/browse/CPS-731>`_ Query based on Public CM Properties
- - `CPS-828 <https://jira.onap.org/browse/CPS-828>`_ Async: NCMP Rest impl. including Request ID generation
- - `CPS-829 <https://jira.onap.org/browse/CPS-829>`_ Async: Internal message topic incl. basic producer & Consumer
- - `CPS-830 <https://jira.onap.org/browse/CPS-830>`_ DMI-NCMP Asynchronously Publish Response Event to Client Topic
- - `CPS-869 <https://jira.onap.org/browse/CPS-869>`_ Apply Standardized logging fields to adhere to ONAP Best practice REQ-1072
- - `CPS-870 <https://jira.onap.org/browse/CPS-870>`_ Align CPS-Core output with SDN-C output (add module name)
- - `CPS-875 <https://jira.onap.org/browse/CPS-875>`_ CM Handle State: Watchdog-process that syncs 'ADVISED' CM Handles
- - `CPS-877 <https://jira.onap.org/browse/CPS-877>`_ CM Handle State: Exclude any CM-Handles from queries/operations that are not in state 'READY'
- - `CPS-899 <https://jira.onap.org/browse/CPS-899>`_ Start and stop sessions on Java API
- - `CPS-909 <https://jira.onap.org/browse/CPS-909>`_ Separate NCMP endpoint for ch/{cm-handle}/properties and ch/{cm-handle}/state
- - `CPS-917 <https://jira.onap.org/browse/CPS-917>`_ Structured Errors response for passthrough use-cases in NCMP
- - `CPS-953 <https://jira.onap.org/browse/CPS-953>`_ Update maven deploy plugin version
- - `CPS-977 <https://jira.onap.org/browse/CPS-977>`_ Query CM Handles using CpsPath
- - `CPS-1000 <https://jira.onap.org/browse/CPS-1000>`_ Create Data Synchronization watchdog
- - `CPS-1016 <https://jira.onap.org/browse/CPS-1016>`_ Merge 2 'query' end points in NCMP
- - `CPS-1034 <https://jira.onap.org/browse/CPS-1034>`_ Publish lifecycle events for ADVISED , READY and LOCKED state transition"
- - `CPS-1064 <https://jira.onap.org/browse/CPS-1064>`_ Support retrieval of YANG module sources for CM handle on the NCMP interface
- - `CPS-1099 <https://jira.onap.org/browse/CPS-1099>`_ Expose simplified 'external' lock reason enum state over REST interface
- - `CPS-1101 <https://jira.onap.org/browse/CPS-1101>`_ Introducing the DELETING and DELETED Cmhandle State
- - `CPS-1102 <https://jira.onap.org/browse/CPS-1102>`_ Register the Cmhandle Sends Advised State notification.
- - `CPS-1133 <https://jira.onap.org/browse/CPS-1133>`_ Enable/Disable Data Sync for Cm Handle
- - `CPS-1136 <https://jira.onap.org/browse/CPS-1136>`_ DMI Audit Support (get all CM Handles for a registered DMI)
+ - `CPS-340 <https://lf-onap.atlassian.net/browse/CPS-340>`_ Patch and update the root data node
+ - `CPS-575 <https://lf-onap.atlassian.net/browse/CPS-575>`_ Write data for cmHandle using ncmp-datastores:passthrough-running (NCMP.)
+ - `CPS-731 <https://lf-onap.atlassian.net/browse/CPS-731>`_ Query based on Public CM Properties
+ - `CPS-828 <https://lf-onap.atlassian.net/browse/CPS-828>`_ Async: NCMP Rest impl. including Request ID generation
+ - `CPS-829 <https://lf-onap.atlassian.net/browse/CPS-829>`_ Async: Internal message topic incl. basic producer & Consumer
+ - `CPS-830 <https://lf-onap.atlassian.net/browse/CPS-830>`_ DMI-NCMP Asynchronously Publish Response Event to Client Topic
+ - `CPS-869 <https://lf-onap.atlassian.net/browse/CPS-869>`_ Apply Standardized logging fields to adhere to ONAP Best practice REQ-1072
+ - `CPS-870 <https://lf-onap.atlassian.net/browse/CPS-870>`_ Align CPS-Core output with SDN-C output (add module name)
+ - `CPS-875 <https://lf-onap.atlassian.net/browse/CPS-875>`_ CM Handle State: Watchdog-process that syncs 'ADVISED' CM Handles
+ - `CPS-877 <https://lf-onap.atlassian.net/browse/CPS-877>`_ CM Handle State: Exclude any CM-Handles from queries/operations that are not in state 'READY'
+ - `CPS-899 <https://lf-onap.atlassian.net/browse/CPS-899>`_ Start and stop sessions on Java API
+ - `CPS-909 <https://lf-onap.atlassian.net/browse/CPS-909>`_ Separate NCMP endpoint for ch/{cm-handle}/properties and ch/{cm-handle}/state
+ - `CPS-917 <https://lf-onap.atlassian.net/browse/CPS-917>`_ Structured Errors response for passthrough use-cases in NCMP
+ - `CPS-953 <https://lf-onap.atlassian.net/browse/CPS-953>`_ Update maven deploy plugin version
+ - `CPS-977 <https://lf-onap.atlassian.net/browse/CPS-977>`_ Query CM Handles using CpsPath
+ - `CPS-1000 <https://lf-onap.atlassian.net/browse/CPS-1000>`_ Create Data Synchronization watchdog
+ - `CPS-1016 <https://lf-onap.atlassian.net/browse/CPS-1016>`_ Merge 2 'query' end points in NCMP
+ - `CPS-1034 <https://lf-onap.atlassian.net/browse/CPS-1034>`_ Publish lifecycle events for ADVISED , READY and LOCKED state transition"
+ - `CPS-1064 <https://lf-onap.atlassian.net/browse/CPS-1064>`_ Support retrieval of YANG module sources for CM handle on the NCMP interface
+ - `CPS-1099 <https://lf-onap.atlassian.net/browse/CPS-1099>`_ Expose simplified 'external' lock reason enum state over REST interface
+ - `CPS-1101 <https://lf-onap.atlassian.net/browse/CPS-1101>`_ Introducing the DELETING and DELETED Cmhandle State
+ - `CPS-1102 <https://lf-onap.atlassian.net/browse/CPS-1102>`_ Register the Cmhandle Sends Advised State notification.
+ - `CPS-1133 <https://lf-onap.atlassian.net/browse/CPS-1133>`_ Enable/Disable Data Sync for Cm Handle
+ - `CPS-1136 <https://lf-onap.atlassian.net/browse/CPS-1136>`_ DMI Audit Support (get all CM Handles for a registered DMI)
Bug Fixes
---------
- - `CPS-896 <https://jira.onap.org/browse/CPS-896>`_ CM Handle Registration Process only partially completes when exception is thrown
- - `CPS-957 <https://jira.onap.org/browse/CPS-957>`_ NCMP: fix getResourceDataForPassthroughOperational endpoint
- - `CPS-1020 <https://jira.onap.org/browse/CPS-1020>`_ DuplicatedYangResourceException error at parallel cmHandle registration
- - `CPS-1056 <https://jira.onap.org/browse/CPS-1056>`_ Wrong error response format in case of Dmi plugin error
- - `CPS-1067 <https://jira.onap.org/browse/CPS-1067>`_ NCMP returns 500 error on searches endpoint when No DMI Handles registered
- - `CPS-1085 <https://jira.onap.org/browse/CPS-1085>`_ Performance degradation on ncmp/v1/ch/searches endpoint
- - `CPS-1088 <https://jira.onap.org/browse/CPS-1088>`_ Kafka consumer can not be turned off
- - `CPS-1097 <https://jira.onap.org/browse/CPS-1097>`_ Unable to change state from LOCKED to ADVISED
- - `CPS-1126 <https://jira.onap.org/browse/CPS-1126>`_ CmHandle creation performance degradation
- - `CPS-1175 <https://jira.onap.org/browse/CPS-1175>`_ Incorrect response when empty body executed for cmhandle id-searches
- - `CPS-1179 <https://jira.onap.org/browse/CPS-1179>`_ Node API - GET method returns invalid response when identifier contains '/'
- - `CPS-1212 <https://jira.onap.org/browse/CPS-1212>`_ Additional Properties for CM Handles not included when send to DMI Plugin
- - `CPS-1217 <https://jira.onap.org/browse/CPS-1217>`_ Searches endpoint gives back empty list however there are already available cmhandles
- - `CPS-1218 <https://jira.onap.org/browse/CPS-1218>`_ NCMP logs are flooded with SyncUtils logs
+ - `CPS-896 <https://lf-onap.atlassian.net/browse/CPS-896>`_ CM Handle Registration Process only partially completes when exception is thrown
+ - `CPS-957 <https://lf-onap.atlassian.net/browse/CPS-957>`_ NCMP: fix getResourceDataForPassthroughOperational endpoint
+ - `CPS-1020 <https://lf-onap.atlassian.net/browse/CPS-1020>`_ DuplicatedYangResourceException error at parallel cmHandle registration
+ - `CPS-1056 <https://lf-onap.atlassian.net/browse/CPS-1056>`_ Wrong error response format in case of Dmi plugin error
+ - `CPS-1067 <https://lf-onap.atlassian.net/browse/CPS-1067>`_ NCMP returns 500 error on searches endpoint when No DMI Handles registered
+ - `CPS-1085 <https://lf-onap.atlassian.net/browse/CPS-1085>`_ Performance degradation on ncmp/v1/ch/searches endpoint
+ - `CPS-1088 <https://lf-onap.atlassian.net/browse/CPS-1088>`_ Kafka consumer can not be turned off
+ - `CPS-1097 <https://lf-onap.atlassian.net/browse/CPS-1097>`_ Unable to change state from LOCKED to ADVISED
+ - `CPS-1126 <https://lf-onap.atlassian.net/browse/CPS-1126>`_ CmHandle creation performance degradation
+ - `CPS-1175 <https://lf-onap.atlassian.net/browse/CPS-1175>`_ Incorrect response when empty body executed for cmhandle id-searches
+ - `CPS-1179 <https://lf-onap.atlassian.net/browse/CPS-1179>`_ Node API - GET method returns invalid response when identifier contains '/'
+ - `CPS-1212 <https://lf-onap.atlassian.net/browse/CPS-1212>`_ Additional Properties for CM Handles not included when send to DMI Plugin
+ - `CPS-1217 <https://lf-onap.atlassian.net/browse/CPS-1217>`_ Searches endpoint gives back empty list however there are already available cmhandles
+ - `CPS-1218 <https://lf-onap.atlassian.net/browse/CPS-1218>`_ NCMP logs are flooded with SyncUtils logs
Known Limitations, Issues and Workarounds
-----------------------------------------
@@ -1144,7 +1275,7 @@ Security Notes
*Fixed Security Issues*
- - `CPS-963 <https://jira.onap.org/browse/CPS-963>`_ Liquibase has got serious vulnerability, upgrade required
+ - `CPS-963 <https://lf-onap.atlassian.net/browse/CPS-963>`_ Liquibase has got serious vulnerability, upgrade required
*Known Security Issues*
@@ -1176,7 +1307,7 @@ Release Data
Bug Fixes
---------
- - `CPS-961 <https://jira.onap.org/browse/CPS-961>`_ Updated ANTLR compiler version to 4.9.2 to be compatible with runtime version
+ - `CPS-961 <https://lf-onap.atlassian.net/browse/CPS-961>`_ Updated ANTLR compiler version to 4.9.2 to be compatible with runtime version
Version: 3.0.0
==============
@@ -1200,31 +1331,31 @@ Release Data
Features
--------
- - `CPS-559 <https://jira.onap.org/browse/CPS-559>`_ Define response objects (schemas) in cps-ncmp
- - `CPS-636 <https://jira.onap.org/browse/CPS-636>`_ Update operation for datastore pass through running
- - `CPS-638 <https://jira.onap.org/browse/CPS-638>`_ Delete operation for datastore pass through running
- - `CPS-677 <https://jira.onap.org/browse/CPS-677>`_ Support 'public' Cm Handle Properties
- - `CPS-741 <https://jira.onap.org/browse/CPS-741>`_ Re sync after removing cm handles
- - `CPS-777 <https://jira.onap.org/browse/CPS-777>`_ Ensure all DMI operations use POST method
- - `CPS-780 <https://jira.onap.org/browse/CPS-780>`_ Add examples for parameters, request and response in openapi yaml for cps-core
- - `CPS-789 <https://jira.onap.org/browse/CPS-789>`_ CPS Data Updated Event Schema V2 to support delete operation
- - `CPS-791 <https://jira.onap.org/browse/CPS-791>`_ CPS-Core sends delete notification event
- - `CPS-817 <https://jira.onap.org/browse/CPS-817>`_ Create Endpoint For Get Cm Handles (incl. public properties) By Name
- - `CPS-837 <https://jira.onap.org/browse/CPS-837>`_ Add Remove and Update properties (DMI and Public) as part of CM Handle Registration update
+ - `CPS-559 <https://lf-onap.atlassian.net/browse/CPS-559>`_ Define response objects (schemas) in cps-ncmp
+ - `CPS-636 <https://lf-onap.atlassian.net/browse/CPS-636>`_ Update operation for datastore pass through running
+ - `CPS-638 <https://lf-onap.atlassian.net/browse/CPS-638>`_ Delete operation for datastore pass through running
+ - `CPS-677 <https://lf-onap.atlassian.net/browse/CPS-677>`_ Support 'public' Cm Handle Properties
+ - `CPS-741 <https://lf-onap.atlassian.net/browse/CPS-741>`_ Re sync after removing cm handles
+ - `CPS-777 <https://lf-onap.atlassian.net/browse/CPS-777>`_ Ensure all DMI operations use POST method
+ - `CPS-780 <https://lf-onap.atlassian.net/browse/CPS-780>`_ Add examples for parameters, request and response in openapi yaml for cps-core
+ - `CPS-789 <https://lf-onap.atlassian.net/browse/CPS-789>`_ CPS Data Updated Event Schema V2 to support delete operation
+ - `CPS-791 <https://lf-onap.atlassian.net/browse/CPS-791>`_ CPS-Core sends delete notification event
+ - `CPS-817 <https://lf-onap.atlassian.net/browse/CPS-817>`_ Create Endpoint For Get Cm Handles (incl. public properties) By Name
+ - `CPS-837 <https://lf-onap.atlassian.net/browse/CPS-837>`_ Add Remove and Update properties (DMI and Public) as part of CM Handle Registration update
Bug Fixes
---------
- - `CPS-762 <https://jira.onap.org/browse/CPS-762>`_ Query cm handles for module names returns incorrect cm handle identifiers
- - `CPS-788 <https://jira.onap.org/browse/CPS-788>`_ Yang Resource formatting is incorrect
- - `CPS-783 <https://jira.onap.org/browse/CPS-783>`_ Remove cm handle does not completely remove all cm handle information
- - `CPS-841 <https://jira.onap.org/browse/CPS-841>`_ Upgrade log4j to 2.17.1 as recommended by ONAP SECCOM
- - `CPS-856 <https://jira.onap.org/browse/CPS-856>`_ Retry mechanism not working for concurrent CmHandle registration
- - `CPS-867 <https://jira.onap.org/browse/CPS-867>`_ Database port made configurable through env variable DB_PORT
- - `CPS-886 <https://jira.onap.org/browse/CPS-886>`_ Fragment handling decreasing performance for large number of cmHandles
- - `CPS-887 <https://jira.onap.org/browse/CPS-887>`_ Increase performance of cmHandle registration for large number of schema sets in DB
- - `CPS-892 <https://jira.onap.org/browse/CPS-892>`_ Fixed the response code during CM-Handle Registration from 201 CREATED to 204 NO_CONTENT
- - `CPS-893 <https://jira.onap.org/browse/CPS-893>`_ NCMP Java API depends on NCMP-Rest-API (cyclic) through json properties on Java API
+ - `CPS-762 <https://lf-onap.atlassian.net/browse/CPS-762>`_ Query cm handles for module names returns incorrect cm handle identifiers
+ - `CPS-788 <https://lf-onap.atlassian.net/browse/CPS-788>`_ Yang Resource formatting is incorrect
+ - `CPS-783 <https://lf-onap.atlassian.net/browse/CPS-783>`_ Remove cm handle does not completely remove all cm handle information
+ - `CPS-841 <https://lf-onap.atlassian.net/browse/CPS-841>`_ Upgrade log4j to 2.17.1 as recommended by ONAP SECCOM
+ - `CPS-856 <https://lf-onap.atlassian.net/browse/CPS-856>`_ Retry mechanism not working for concurrent CmHandle registration
+ - `CPS-867 <https://lf-onap.atlassian.net/browse/CPS-867>`_ Database port made configurable through env variable DB_PORT
+ - `CPS-886 <https://lf-onap.atlassian.net/browse/CPS-886>`_ Fragment handling decreasing performance for large number of cmHandles
+ - `CPS-887 <https://lf-onap.atlassian.net/browse/CPS-887>`_ Increase performance of cmHandle registration for large number of schema sets in DB
+ - `CPS-892 <https://lf-onap.atlassian.net/browse/CPS-892>`_ Fixed the response code during CM-Handle Registration from 201 CREATED to 204 NO_CONTENT
+ - `CPS-893 <https://lf-onap.atlassian.net/browse/CPS-893>`_ NCMP Java API depends on NCMP-Rest-API (cyclic) through json properties on Java API
Known Limitations, Issues and Workarounds
-----------------------------------------
@@ -1232,10 +1363,10 @@ Known Limitations, Issues and Workarounds
*System Limitations*
Null can no longer be passed within the dmi plugin service names when registering a cm handle, as part of
-`CPS-837 <https://jira.onap.org/browse/CPS-837>`_ null is now used to indicate if a property should be removed as part
+`CPS-837 <https://lf-onap.atlassian.net/browse/CPS-837>`_ null is now used to indicate if a property should be removed as part
of cm handle registration.
-The Absolute path to list with integer key will not work. Please refer `CPS-961 <https://jira.onap.org/browse/CPS-961>`_
+The Absolute path to list with integer key will not work. Please refer `CPS-961 <https://lf-onap.atlassian.net/browse/CPS-961>`_
for more information.
*Known Vulnerabilities*
@@ -1285,8 +1416,8 @@ Release Data
Bug Fixes
---------
- - `CPS-879 <https://jira.onap.org/browse/CPS-879>`_ Fix docker compose for csit test.
- - `CPS-873 <https://jira.onap.org/browse/CPS-873>`_ Fix intermittent circular dependency error when the application starts.
+ - `CPS-879 <https://lf-onap.atlassian.net/browse/CPS-879>`_ Fix docker compose for csit test.
+ - `CPS-873 <https://lf-onap.atlassian.net/browse/CPS-873>`_ Fix intermittent circular dependency error when the application starts.
Version: 2.0.3
==============
@@ -1311,7 +1442,7 @@ Release Data
Bug Fixes
---------
- - `CPS-841 <https://jira.onap.org/browse/CPS-841>`_ Update log4j version to 2.17.1 due to security vulnerability
+ - `CPS-841 <https://lf-onap.atlassian.net/browse/CPS-841>`_ Update log4j version to 2.17.1 due to security vulnerability
Version: 2.0.2
==============
@@ -1336,7 +1467,7 @@ Release Data
Bug Fixes
---------
- - `CPS-820 <https://jira.onap.org/browse/CPS-820>`_ Update log4j version due to security vulnerability
+ - `CPS-820 <https://lf-onap.atlassian.net/browse/CPS-820>`_ Update log4j version due to security vulnerability
Version: 2.0.1
==============
@@ -1361,10 +1492,10 @@ Release Data
Bug Fixes
---------
- - `CPS-594 <https://jira.onap.org/browse/CPS-594>`_ SQL ConstraintViolationException when updating the list node element using PATCH List node API
- - `CPS-653 <https://jira.onap.org/browse/CPS-653>`_ cmHandleProperties not supported by dmi in fetch modules
- - `CPS-673 <https://jira.onap.org/browse/CPS-673>`_ Improvement and cleanup for CPS Core charts
- - `CPS-691 <https://jira.onap.org/browse/CPS-691>`_ NCMP no master index label on index documentation page
+ - `CPS-594 <https://lf-onap.atlassian.net/browse/CPS-594>`_ SQL ConstraintViolationException when updating the list node element using PATCH List node API
+ - `CPS-653 <https://lf-onap.atlassian.net/browse/CPS-653>`_ cmHandleProperties not supported by dmi in fetch modules
+ - `CPS-673 <https://lf-onap.atlassian.net/browse/CPS-673>`_ Improvement and cleanup for CPS Core charts
+ - `CPS-691 <https://lf-onap.atlassian.net/browse/CPS-691>`_ NCMP no master index label on index documentation page
Known Limitations, Issues and Workarounds
-----------------------------------------
@@ -1375,7 +1506,7 @@ Limitations to the amount of child nodes that can be added to the fix above. The
*Known Vulnerabilities*
- - `CPS-725 <https://jira.onap.org/browse/CPS-725>`_ fix sample docker compose of cps/ncmp and onap dmi plugin
+ - `CPS-725 <https://lf-onap.atlassian.net/browse/CPS-725>`_ fix sample docker compose of cps/ncmp and onap dmi plugin
*Workarounds*
@@ -1386,7 +1517,7 @@ Security Notes
*Fixed Security Issues*
- - `CPS-581 <https://jira.onap.org/browse/CPS-581>`_ Remove security vulnerabilities
+ - `CPS-581 <https://lf-onap.atlassian.net/browse/CPS-581>`_ Remove security vulnerabilities
*Known Security Issues*
@@ -1438,32 +1569,32 @@ Software Deliverables
Bug Fixes
---------
- - `CPS-310 <https://jira.onap.org/browse/CPS-310>`_ Data schema migration from Honolulu to Istanbul is failing
- - `CPS-316 <https://jira.onap.org/browse/CPS-316>`_ Xpath cannot be created for augmentation data node
- - `CPS-336 <https://jira.onap.org/browse/CPS-336>`_ Ends-with functionality in cpsPath does not conform with standard xPath behavior
- - `CPS-345 <https://jira.onap.org/browse/CPS-345>`_ Leaf String value comparison matches mix of single and double quotes
- - `CPS-357 <https://jira.onap.org/browse/CPS-357>`_ cps-review-verification-maven-master Jenkins job is failing when running csit test
- - `CPS-367 <https://jira.onap.org/browse/CPS-367>`_ Get descendent does not support xpaths that end in list values
- - `CPS-377 <https://jira.onap.org/browse/CPS-377>`_ Init ran model validation is failing error details are not provided
- - `CPS-422 <https://jira.onap.org/browse/CPS-422>`_ REST 404 response returned instead of 400 for POST/PUT/PATCH request types
- - `CPS-450 <https://jira.onap.org/browse/CPS-450>`_ Datanode query using full path to node causes NPE
- - `CPS-451 <https://jira.onap.org/browse/CPS-451>`_ cps-ran-schema-model@2021-01-28.yang missing root container
- - `CPS-464 <https://jira.onap.org/browse/CPS-464>`_ Request to update node leaves (patch) responds with Internal Server Error
- - `CPS-465 <https://jira.onap.org/browse/CPS-465>`_ Request to update node leaves (patch) responds with json parsing failure
- - `CPS-466 <https://jira.onap.org/browse/CPS-466>`_ Concurrent requests to create schema sets for the same yang model are not supported
- - `CPS-479 <https://jira.onap.org/browse/CPS-479>`_ Get Nodes API does not always return the object from the root
- - `CPS-500 <https://jira.onap.org/browse/CPS-500>`_ Special Character Limitations of cpsPath Queries
- - `CPS-501 <https://jira.onap.org/browse/CPS-501>`_ Put DataNode API has missing transaction and error handling for concurrency issues
- - `CPS-524 <https://jira.onap.org/browse/CPS-524>`_ Issue with CPSData API to add an item to an existing list node
- - `CPS-560 <https://jira.onap.org/browse/CPS-560>`_ Response from cps query using text() contains escape characters
- - `CPS-566 <https://jira.onap.org/browse/CPS-566>`_ Can't access grandparent node through ancestor axis
- - `CPS-573 <https://jira.onap.org/browse/CPS-573>`_ /v1/ch/PNFDemo1/modules returning 401 unauthorised.
- - `CPS-587 <https://jira.onap.org/browse/CPS-587>`_ cps-ncmp-service NullpointerException when DmiPluginRegistration has no additionProperties
- - `CPS-591 <https://jira.onap.org/browse/CPS-591>`_ CPS-Core Leaf stored as integer is being returned from DB as float
- - `CPS-601 <https://jira.onap.org/browse/CPS-601>`_ CPS swagger-ui does not show NCMP endpoints
- - `CPS-616 <https://jira.onap.org/browse/CPS-616>`_ NCMP base path does not conform to agreed API URL
- - `CPS-630 <https://jira.onap.org/browse/CPS-630>`_ Incorrect information sent when same anchor is updated faster than notification service processes
- - `CPS-635 <https://jira.onap.org/browse/CPS-635>`_ Module Resource call does not include body
+ - `CPS-310 <https://lf-onap.atlassian.net/browse/CPS-310>`_ Data schema migration from Honolulu to Istanbul is failing
+ - `CPS-316 <https://lf-onap.atlassian.net/browse/CPS-316>`_ Xpath cannot be created for augmentation data node
+ - `CPS-336 <https://lf-onap.atlassian.net/browse/CPS-336>`_ Ends-with functionality in cpsPath does not conform with standard xPath behavior
+ - `CPS-345 <https://lf-onap.atlassian.net/browse/CPS-345>`_ Leaf String value comparison matches mix of single and double quotes
+ - `CPS-357 <https://lf-onap.atlassian.net/browse/CPS-357>`_ cps-review-verification-maven-master Jenkins job is failing when running csit test
+ - `CPS-367 <https://lf-onap.atlassian.net/browse/CPS-367>`_ Get descendent does not support xpaths that end in list values
+ - `CPS-377 <https://lf-onap.atlassian.net/browse/CPS-377>`_ Init ran model validation is failing error details are not provided
+ - `CPS-422 <https://lf-onap.atlassian.net/browse/CPS-422>`_ REST 404 response returned instead of 400 for POST/PUT/PATCH request types
+ - `CPS-450 <https://lf-onap.atlassian.net/browse/CPS-450>`_ Datanode query using full path to node causes NPE
+ - `CPS-451 <https://lf-onap.atlassian.net/browse/CPS-451>`_ cps-ran-schema-model@2021-01-28.yang missing root container
+ - `CPS-464 <https://lf-onap.atlassian.net/browse/CPS-464>`_ Request to update node leaves (patch) responds with Internal Server Error
+ - `CPS-465 <https://lf-onap.atlassian.net/browse/CPS-465>`_ Request to update node leaves (patch) responds with json parsing failure
+ - `CPS-466 <https://lf-onap.atlassian.net/browse/CPS-466>`_ Concurrent requests to create schema sets for the same yang model are not supported
+ - `CPS-479 <https://lf-onap.atlassian.net/browse/CPS-479>`_ Get Nodes API does not always return the object from the root
+ - `CPS-500 <https://lf-onap.atlassian.net/browse/CPS-500>`_ Special Character Limitations of cpsPath Queries
+ - `CPS-501 <https://lf-onap.atlassian.net/browse/CPS-501>`_ Put DataNode API has missing transaction and error handling for concurrency issues
+ - `CPS-524 <https://lf-onap.atlassian.net/browse/CPS-524>`_ Issue with CPSData API to add an item to an existing list node
+ - `CPS-560 <https://lf-onap.atlassian.net/browse/CPS-560>`_ Response from cps query using text() contains escape characters
+ - `CPS-566 <https://lf-onap.atlassian.net/browse/CPS-566>`_ Can't access grandparent node through ancestor axis
+ - `CPS-573 <https://lf-onap.atlassian.net/browse/CPS-573>`_ /v1/ch/PNFDemo1/modules returning 401 unauthorised.
+ - `CPS-587 <https://lf-onap.atlassian.net/browse/CPS-587>`_ cps-ncmp-service NullpointerException when DmiPluginRegistration has no additionProperties
+ - `CPS-591 <https://lf-onap.atlassian.net/browse/CPS-591>`_ CPS-Core Leaf stored as integer is being returned from DB as float
+ - `CPS-601 <https://lf-onap.atlassian.net/browse/CPS-601>`_ CPS swagger-ui does not show NCMP endpoints
+ - `CPS-616 <https://lf-onap.atlassian.net/browse/CPS-616>`_ NCMP base path does not conform to agreed API URL
+ - `CPS-630 <https://lf-onap.atlassian.net/browse/CPS-630>`_ Incorrect information sent when same anchor is updated faster than notification service processes
+ - `CPS-635 <https://lf-onap.atlassian.net/browse/CPS-635>`_ Module Resource call does not include body
This document provides the release notes for Istanbul release.
@@ -1502,9 +1633,9 @@ Limitations to the amount of child nodes that can be added to the fix above. The
*Known Vulnerabilities*
- - `CPS-594 <https://jira.onap.org/browse/CPS-594>`_ SQL ConstraintViolationException when updating the list node element using PATCH List node API
- - `CPS-653 <https://jira.onap.org/browse/CPS-653>`_ cmHandleProperties not supported by dmi in fetch modules
- - `CPS-673 <https://jira.onap.org/browse/CPS-673>`_ Improvement and cleanup for CPS Core charts
+ - `CPS-594 <https://lf-onap.atlassian.net/browse/CPS-594>`_ SQL ConstraintViolationException when updating the list node element using PATCH List node API
+ - `CPS-653 <https://lf-onap.atlassian.net/browse/CPS-653>`_ cmHandleProperties not supported by dmi in fetch modules
+ - `CPS-673 <https://lf-onap.atlassian.net/browse/CPS-673>`_ Improvement and cleanup for CPS Core charts
*Workarounds*
@@ -1515,11 +1646,11 @@ Security Notes
*Fixed Security Issues*
- - `CPS-249 <https://jira.onap.org/browse/CPS-249>`_ Exception stack trace is exposed
+ - `CPS-249 <https://lf-onap.atlassian.net/browse/CPS-249>`_ Exception stack trace is exposed
*Known Security Issues*
- - `CPS-581 <https://jira.onap.org/browse/CPS-581>`_ Remove security vulnerabilities
+ - `CPS-581 <https://lf-onap.atlassian.net/browse/CPS-581>`_ Remove security vulnerabilities
Test Results
------------
@@ -1552,25 +1683,25 @@ Release Data
Bug Fixes
---------
- - `CPS-706 <https://jira.onap.org/browse/CPS-706>`_ Get moduleschema/yangresouce endpoint not working
- - `CPS-276 <https://jira.onap.org/browse/CPS-276>`_ Improve error reporting for invalid cpsPath on Queries
- - `CPS-288 <https://jira.onap.org/browse/CPS-288>`_ Move security configuration to the application module
- - `CPS-290 <https://jira.onap.org/browse/CPS-290>`_ Internal Server Error when creating the same data node twice
- - `CPS-292 <https://jira.onap.org/browse/CPS-292>`_ Detailed information is missing to explain why data is not compliant with the specified YANG model
- - `CPS-300 <https://jira.onap.org/browse/CPS-304>`_ Not able to create data instances for 2 different anchors using the same model
- - `CPS-304 <https://jira.onap.org/browse/CPS-304>`_ Use ONAP recommended base Java Docker image
- - `CPS-308 <https://jira.onap.org/browse/CPS-308>`_ Not able to upload yang models files greater than 1MB
+ - `CPS-706 <https://lf-onap.atlassian.net/browse/CPS-706>`_ Get moduleschema/yangresouce endpoint not working
+ - `CPS-276 <https://lf-onap.atlassian.net/browse/CPS-276>`_ Improve error reporting for invalid cpsPath on Queries
+ - `CPS-288 <https://lf-onap.atlassian.net/browse/CPS-288>`_ Move security configuration to the application module
+ - `CPS-290 <https://lf-onap.atlassian.net/browse/CPS-290>`_ Internal Server Error when creating the same data node twice
+ - `CPS-292 <https://lf-onap.atlassian.net/browse/CPS-292>`_ Detailed information is missing to explain why data is not compliant with the specified YANG model
+ - `CPS-300 <https://lf-onap.atlassian.net/browse/CPS-304>`_ Not able to create data instances for 2 different anchors using the same model
+ - `CPS-304 <https://lf-onap.atlassian.net/browse/CPS-304>`_ Use ONAP recommended base Java Docker image
+ - `CPS-308 <https://lf-onap.atlassian.net/browse/CPS-308>`_ Not able to upload yang models files greater than 1MB
Security Notes
--------------
*Fixed Security Issues*
- - `CPS-249 <https://jira.onap.org/browse/CPS-249>`_ Exception stack trace is exposed
+ - `CPS-249 <https://lf-onap.atlassian.net/browse/CPS-249>`_ Exception stack trace is exposed
*Known Security Issues*
- - `Security Waiver <https://wiki.onap.org/display/DW/Honolulu+Exception+Request+for+CPS>`_ Security - Expose external endpoints with https
+ - `Security Waiver <https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16467851/Honolulu+Exception+Request+for+CPS>`_ Security - Expose external endpoints with https
Version: 1.0.0
==============
@@ -1649,10 +1780,10 @@ Software Deliverables
Known Limitations, Issues and Workarounds
-----------------------------------------
- - `CPS-249 <https://jira.onap.org/browse/CPS-249>`_ Exception stack trace is exposed
- - `CPS-264 <https://jira.onap.org/browse/CPS-264>`_ Unique timestamp is missing when tagging docker images.
+ - `CPS-249 <https://lf-onap.atlassian.net/browse/CPS-249>`_ Exception stack trace is exposed
+ - `CPS-264 <https://lf-onap.atlassian.net/browse/CPS-264>`_ Unique timestamp is missing when tagging docker images.
- Methods exposed on API which are yet not implemented : deleteAnchor, getNodesByDataspace & deleteDataspace.
- - `CPS-465 <https://jira.onap.org/browse/CPS-465>`_ & `CPS-464 <https://jira.onap.org/browse/CPS-464>`_ Update data node leaves API does not support updating a list element with compound keys.
+ - `CPS-465 <https://lf-onap.atlassian.net/browse/CPS-465>`_ & `CPS-464 <https://lf-onap.atlassian.net/browse/CPS-464>`_ Update data node leaves API does not support updating a list element with compound keys.
*System Limitations*
@@ -1671,7 +1802,7 @@ Security Notes
*Fixed Security Issues*
-* `CPS-167 <https://jira.onap.org/browse/CPS-167>`_ -Update CPS dependencies as Required for Honolulu release
+* `CPS-167 <https://lf-onap.atlassian.net/browse/CPS-167>`_ -Update CPS dependencies as Required for Honolulu release
- Upgrade org.onap.oparent to 3.2.0
- Upgrade spring.boot to 2.3.8.RELEASE
- Upgrade yangtools to 5.0.7
@@ -1689,7 +1820,7 @@ CPS code has been formally scanned during build time using NexusIQ and all Criti
Test Results
------------
- * `Integration tests <https://wiki.onap.org/display/DW/CPS+Integration+Test+Cases>`_
+ * `Integration tests <https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16462943/CPS+Integration+Test+Cases>`_
References
----------
@@ -1704,12 +1835,12 @@ For more information on the latest ONAP release, please see:
.. _`ONAP Home Page`: https://www.onap.org
-.. _`ONAP Wiki Page`: https://wiki.onap.org
+.. _`ONAP Wiki Page`: https://lf-onap.atlassian.net
.. _`ONAP Documentation`: https://docs.onap.org
.. _`ONAP CPS Documentation`: https://docs.onap.org/projects/onap-cps
.. _`ONAP Release Downloads`: https://git.onap.org
Quick Links:
- - `CPS project page <https://wiki.onap.org/pages/viewpage.action?pageId=71834216>`_
+ - `CPS project page <https://lf-onap.atlassian.net/wiki/spaces/DW/overview>`_
- `Passing Badge information for CPS <https://bestpractices.coreinfrastructure.org/en/projects/4398>`_
diff --git a/docs/schemas/ncmp-in-event-schema-1.0.0.json b/docs/schemas/ncmp-in-event-schema-1.0.0.json
new file mode 100644
index 0000000000..f8b6c2e680
--- /dev/null
+++ b/docs/schemas/ncmp-in-event-schema-1.0.0.json
@@ -0,0 +1,73 @@
+{
+ "$id": "urn:cps:org.onap.cps.ncmp.events:cm-notification-subscription-ncmp-in-event:1.0.0",
+ "$ref": "#/definitions/NcmpInEvent",
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "definitions": {
+ "NcmpInEvent": {
+ "description": "The payload for subscription merge event.",
+ "javaType": "org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.NcmpInEvent",
+ "properties": {
+ "data": {
+ "properties": {
+ "subscriptionId": {
+ "description": "The subscription details.",
+ "type": "string"
+ },
+ "predicates": {
+ "type": "array",
+ "description": "Additional values to be added into the subscription",
+ "items": {
+ "type": "object",
+ "properties": {
+ "targetFilter": {
+ "description": "CM Handles to be targeted by the subscription",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "scopeFilter": {
+ "type": "object",
+ "properties": {
+ "datastore": {
+ "description": "Datastore which is to be used by the subscription",
+ "type": "string",
+ "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"]
+ },
+ "xpathFilter": {
+ "description": "Filter to be applied to the CM Handles through this event",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "xpathFilter"
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "targetFilter"
+ ]
+ },
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "subscriptionId"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ }
+ },
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "data"
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/docs/schemas/ncmp-out-event-schema-1.0.0.json b/docs/schemas/ncmp-out-event-schema-1.0.0.json
new file mode 100644
index 0000000000..d6ef55d063
--- /dev/null
+++ b/docs/schemas/ncmp-out-event-schema-1.0.0.json
@@ -0,0 +1,63 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.events:cm-notification-subscription-ncmp-out-event-schema:1.0.0",
+ "$ref": "#/definitions/NcmpOutEvent",
+ "definitions": {
+ "NcmpOutEvent": {
+ "type": "object",
+ "description": "The payload applied cm subscription merge event coming out from NCMP.",
+ "javaType": "org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent",
+ "additionalProperties": false,
+ "properties": {
+ "data": {
+ "$ref": "#/definitions/Data"
+ }
+ },
+ "required": [
+ "data"
+ ],
+ "title": "NcmpOutEvent"
+ },
+ "Data": {
+ "type": "object",
+ "description": "Information about the targets and subscription",
+ "additionalProperties": false,
+ "properties": {
+ "subscriptionId": {
+ "type": "string",
+ "description": "The unique subscription id"
+ },
+ "acceptedTargets": {
+ "type": "array",
+ "description": "List of accepted targets",
+ "items": {
+ "type": "string"
+ }
+ },
+ "rejectedTargets": {
+ "type": "array",
+ "description": "List of rejected targets",
+ "items": {
+ "type": "string"
+ }
+ },
+ "pendingTargets": {
+ "type": "array",
+ "description": "List of pending targets",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "required": [
+ "subscriptionId",
+ "acceptedTargets",
+ "rejectedTargets",
+ "pendingTargets"
+ ],
+ "title": "Data"
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json
new file mode 100644
index 0000000000..4d98bc8632
--- /dev/null
+++ b/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-create-schema:1.0.0",
+ "$ref": "#/definitions/NcmpCreate",
+ "definitions": {
+ "NcmpCreate": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cmHandleId": {
+ "type": "string"
+ },
+ "resourceIdentifier": {
+ "type": "string"
+ },
+ "targetIdentifier": {
+ "type": "string"
+ },
+ "cmChangeRequest": {
+ "type": "object"
+ }
+ },
+ "required": [
+ "targetIdentifier",
+ "cmChangeRequest"
+ ]
+ }
+ }
+}
diff --git a/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json
new file mode 100644
index 0000000000..1246d9d815
--- /dev/null
+++ b/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-delete-schema:1.0.0",
+ "$ref": "#/definitions/NcmpDelete",
+ "definitions": {
+ "NcmpDelete": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cmHandleId": {
+ "type": "string"
+ },
+ "resourceIdentifier": {
+ "type": "string"
+ },
+ "targetIdentifier": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "targetIdentifier"
+ ]
+ }
+ }
+}
diff --git a/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json
new file mode 100644
index 0000000000..4917aea146
--- /dev/null
+++ b/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-patch-schema:1.0.0",
+ "$ref": "#/definitions/NcmpPatch",
+ "definitions": {
+ "NcmpPatch": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cmHandleId": {
+ "type": "string"
+ },
+ "resourceIdentifier": {
+ "type": "string"
+ },
+ "targetIdentifier": {
+ "type": "string"
+ },
+ "cmChangeRequest": {
+ "type": "object"
+ }
+ },
+ "required": [
+ "targetIdentifier",
+ "cmChangeRequest"
+ ]
+ }
+ }
+}
diff --git a/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json
new file mode 100644
index 0000000000..831526cd52
--- /dev/null
+++ b/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-update-schema:1.0.0",
+ "$ref": "#/definitions/NcmpUpdate",
+ "definitions": {
+ "NcmpUpdate": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cmHandleId": {
+ "type": "string"
+ },
+ "resourceIdentifier": {
+ "type": "string"
+ },
+ "targetIdentifier": {
+ "type": "string"
+ },
+ "cmChangeRequest": {
+ "type": "object"
+ }
+ },
+ "required": [
+ "targetIdentifier",
+ "cmChangeRequest"
+ ]
+ }
+ }
+}
diff --git a/integration-test/pom.xml b/integration-test/pom.xml
index a3112661e9..b39c1987bf 100644
--- a/integration-test/pom.xml
+++ b/integration-test/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -107,6 +107,11 @@
<artifactId>spock</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-json</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<profiles>
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
index 6855e49c54..587cbae619 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
@@ -39,13 +39,14 @@ import org.onap.cps.ncmp.impl.inventory.ParameterizedCmHandleQueryService
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncWatchdog
import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
+import org.onap.cps.ri.repository.DataspaceRepository
+import org.onap.cps.ri.utils.SessionManager
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
import org.onap.cps.spi.model.DataNode
-import org.onap.cps.spi.repository.DataspaceRepository
-import org.onap.cps.spi.utils.SessionManager
import org.onap.cps.utils.ContentType
import org.onap.cps.utils.JsonObjectMapper
import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
@@ -71,7 +72,7 @@ import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY
@AutoConfigureMockMvc
@EnableJpaRepositories(basePackageClasses = [DataspaceRepository])
@ComponentScan(basePackages = ['org.onap.cps'])
-@EntityScan('org.onap.cps.spi.entities')
+@EntityScan('org.onap.cps.ri.models')
abstract class CpsIntegrationSpecBase extends Specification {
@Shared
@@ -125,12 +126,24 @@ abstract class CpsIntegrationSpecBase extends Specification {
@Autowired
AlternateIdMatcher alternateIdMatcher
- MockWebServer mockDmiServer = null
- DmiDispatcher dmiDispatcher = new DmiDispatcher()
- def DMI_URL = null
+ @Value('${ncmp.policy-executor.server.port:8080}')
+ private String policyServerPort;
+
+ MockWebServer mockDmiServer1 = new MockWebServer()
+ MockWebServer mockDmiServer2 = new MockWebServer()
+ MockWebServer mockPolicyServer = new MockWebServer()
+
+ DmiDispatcher dmiDispatcher1 = new DmiDispatcher()
+ DmiDispatcher dmiDispatcher2 = new DmiDispatcher()
+
+ PolicyDispatcher policyDispatcher = new PolicyDispatcher();
+
+ def DMI1_URL = null
+ def DMI2_URL = null
static NO_MODULE_SET_TAG = ''
+ static NO_ALTERNATE_ID = ''
static GENERAL_TEST_DATASPACE = 'generalTestDataspace'
static BOOKSTORE_SCHEMA_SET = 'bookstoreSchemaSet'
static MODULE_SYNC_WAIT_TIME_IN_SECONDS = 10
@@ -144,14 +157,24 @@ abstract class CpsIntegrationSpecBase extends Specification {
createStandardBookStoreSchemaSet(GENERAL_TEST_DATASPACE)
initialized = true
}
- mockDmiServer = new MockWebServer()
- mockDmiServer.setDispatcher(dmiDispatcher)
- mockDmiServer.start()
- DMI_URL = String.format("http://%s:%s", mockDmiServer.getHostName(), mockDmiServer.getPort())
+ mockDmiServer1.setDispatcher(dmiDispatcher1)
+ mockDmiServer1.start()
+
+ mockDmiServer2.setDispatcher(dmiDispatcher2)
+ mockDmiServer2.start()
+
+ mockPolicyServer.setDispatcher(policyDispatcher)
+ mockPolicyServer.start(Integer.valueOf(policyServerPort))
+
+ DMI1_URL = String.format("http://%s:%s", mockDmiServer1.getHostName(), mockDmiServer1.getPort())
+ DMI2_URL = String.format("http://%s:%s", mockDmiServer2.getHostName(), mockDmiServer2.getPort())
+
}
def cleanup() {
- mockDmiServer.shutdown()
+ mockDmiServer1.shutdown()
+ mockDmiServer2.shutdown()
+ mockPolicyServer.shutdown()
}
def static readResourceDataFile(filename) {
@@ -217,7 +240,11 @@ abstract class CpsIntegrationSpecBase extends Specification {
// *** NCMP Integration Test Utilities ***
def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag) {
- def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, moduleSetTag: moduleSetTag)
+ registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag, NO_ALTERNATE_ID)
+ }
+
+ def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag, alternateId) {
+ def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, moduleSetTag: moduleSetTag, alternateId: alternateId)
networkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: [cmHandleToCreate]))
new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
CmHandleState.READY == networkCmProxyInventoryFacade.getCmHandleCompositeState(cmHandleId).cmHandleState
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy
index e77815f5ec..35a7b6a7c2 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy
@@ -20,9 +20,7 @@
package org.onap.cps.integration.base
-import static org.onap.cps.integration.base.CpsIntegrationSpecBase.readResourceDataFile
-
-import java.util.regex.Matcher
+import groovy.json.JsonSlurper
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.RecordedRequest
@@ -30,6 +28,10 @@ import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
+import java.util.regex.Matcher
+
+import static org.onap.cps.integration.base.CpsIntegrationSpecBase.readResourceDataFile
+
/**
* This class simulates responses from the DMI server in NCMP integration tests.
*
@@ -53,8 +55,12 @@ class DmiDispatcher extends Dispatcher {
static final MODULE_RESOURCES_RESPONSE_TEMPLATE = readResourceDataFile('mock-dmi-responses/moduleResourcesTemplate.json')
def isAvailable = true
- Map<String, List<String>> moduleNamesPerCmHandleId = [:]
+
+ def jsonSlurper = new JsonSlurper()
+ def moduleNamesPerCmHandleId = [:]
+ def receivedSubJobs = [:]
def lastAuthHeaderReceived
+ def dmiResourceDataUrl
@Override
MockResponse dispatch(RecordedRequest request) {
@@ -79,43 +85,64 @@ class DmiDispatcher extends Dispatcher {
// pass-through data operation for a CM-handle
case ~'^/dmi/v1/ch/(.*)/data/ds/(.*)$':
+ dmiResourceDataUrl = request.path
return mockResponseWithBody(HttpStatus.OK, '{}')
// legacy pass-through batch data operation
case ~'^/dmi/v1/data$':
return mockResponseWithBody(HttpStatus.ACCEPTED, '{}')
+ // get data job status
+ case ~'^/dmi/v1/cmwriteJob/dataProducer/(.*)/dataProducerJob/(.*)/status$':
+ return mockResponseWithBody(HttpStatus.OK, '{"status":"status details from mock service"}')
+
+ // get data job result
+ case ~'^/dmi/v1/cmwriteJob/dataProducer/(.*)/dataProducerJob/(.*)/result(.*)$':
+ return mockResponseWithBody(HttpStatus.OK, '{ "result": "some result"}')
+
+ // get write sub job response
+ case ~'^/dmi/v1/cmwriteJob(.*)$':
+ return mockWriteJobResponse(request)
+
default:
throw new IllegalArgumentException('Mock DMI does not implement endpoint ' + request.path)
}
}
- private getModuleReferencesResponse(cmHandleId) {
+ def mockWriteJobResponse(request) {
+ def destination = Matcher.lastMatcher[0][1]
+ def subJobWriteRequest = jsonSlurper.parseText(request.getBody().readUtf8())
+ this.receivedSubJobs.put(destination, subJobWriteRequest)
+ def response = '{"subJobId":"some sub job id", "dmiServiceName":"some dmi service name", "dataProducerId":"some data producer id"}'
+ return mockResponseWithBody(HttpStatus.OK, response)
+ }
+
+ def getModuleReferencesResponse(cmHandleId) {
def moduleReferences = '{"schemas":[' + getModuleNamesForCmHandle(cmHandleId).collect {
MODULE_REFERENCES_RESPONSE_TEMPLATE.replaceAll("<MODULE_NAME>", it)
}.join(',') + ']}'
return mockResponseWithBody(HttpStatus.OK, moduleReferences)
}
- private getModuleResourcesResponse(cmHandleId) {
+ def getModuleResourcesResponse(cmHandleId) {
def moduleResources = '[' + getModuleNamesForCmHandle(cmHandleId).collect {
MODULE_RESOURCES_RESPONSE_TEMPLATE.replaceAll("<MODULE_NAME>", it)
}.join(',') + ']'
return mockResponseWithBody(HttpStatus.OK, moduleResources)
}
- private getModuleNamesForCmHandle(cmHandleId) {
+ def getModuleNamesForCmHandle(cmHandleId) {
if (!moduleNamesPerCmHandleId.containsKey(cmHandleId)) {
throw new IllegalArgumentException('Mock DMI has no modules configured for ' + cmHandleId)
}
return moduleNamesPerCmHandleId.get(cmHandleId)
}
- private static mockResponse(status) {
+ def static mockResponse(status) {
return new MockResponse().setResponseCode(status.value())
}
- private static mockResponseWithBody(status, responseBody) {
+ def static mockResponseWithBody(status, responseBody) {
return new MockResponse()
.setResponseCode(status.value())
.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy
new file mode 100644
index 0000000000..b08d1c1548
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy
@@ -0,0 +1,78 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.base
+
+import okhttp3.mockwebserver.Dispatcher
+import okhttp3.mockwebserver.MockResponse
+import okhttp3.mockwebserver.RecordedRequest
+import org.springframework.http.HttpHeaders
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper
+
+import java.util.concurrent.TimeUnit
+
+/**
+ * This class simulates responses from the Policy Execution server in NCMP integration tests.
+ */
+class PolicyDispatcher extends Dispatcher {
+
+ def objectMapper = new ObjectMapper()
+ def expectedAuthorizationToken = 'ABC'
+ def allowAll = true; // Prevents legacy test being affected
+
+ @Override
+ MockResponse dispatch(RecordedRequest recordedRequest) {
+
+ if (!allowAll && !recordedRequest.getHeader('Authorization').contains(expectedAuthorizationToken)) {
+ return new MockResponse().setResponseCode(401)
+ }
+
+ if (recordedRequest.path != '/policy-executor/api/v1/execute') {
+ return new MockResponse().setResponseCode(400)
+ }
+
+ def body = objectMapper.readValue(recordedRequest.getBody().readUtf8(), Map.class)
+ def targetIdentifier = body.get('requests').get(0).get('data').get('targetIdentifier')
+ def responseAsMap = [:]
+ responseAsMap.put('decisionId',1)
+ if (targetIdentifier == "mock slow response") {
+ TimeUnit.SECONDS.sleep(2) // One second more then configured readTimeoutInSeconds
+ }
+ if (allowAll || targetIdentifier == 'fdn1') {
+ responseAsMap.put('decision','allow')
+ responseAsMap.put('message','')
+ } else {
+ responseAsMap.put('decision','deny from mock server (dispatcher)')
+ responseAsMap.put('message','I only like fdn1')
+ }
+ def responseAsString = objectMapper.writeValueAsString(responseAsMap)
+
+ return mockResponseWithBody(HttpStatus.OK, responseAsString)
+ }
+
+ static mockResponseWithBody(status, responseBody) {
+ return new MockResponse()
+ .setResponseCode(status.value())
+ .addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+ .setBody(responseBody)
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy
index a488b3b836..d49931eb7e 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy
@@ -224,7 +224,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'a new (multiple-data-tree:invoice) datanodes'
def json = '{"bookstore-address":[{"bookstore-name":"Easons","address":"Bangalore,India","postal-code":"560043"}]}'
when: 'the new list elements are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/', json, now, ContentType.JSON)
then: 'they can be retrieved by their xpaths'
objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore-address[@bookstore-name="Easons"]', INCLUDE_ALL_DESCENDANTS)
and: 'there is one extra datanode'
@@ -239,7 +239,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'two new (categories) data nodes'
def json = '{"categories": [ {"code":"new1"}, {"code":"new2" } ] }'
when: 'the new list elements are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'they can be retrieved by their xpaths'
objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', DIRECT_CHILDREN_ONLY).size() == 1
objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', DIRECT_CHILDREN_ONLY).size() == 1
@@ -256,7 +256,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'two (categories) data nodes, one new and one existing'
def json = '{"categories": [ {"code":"1"}, {"code":"new1"} ] }'
when: 'attempt to save the list element'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'an exception that (one cps paths is) already defined is thrown '
def exceptionThrown = thrown(AlreadyDefinedException)
exceptionThrown.alreadyDefinedObjectNames == ['/bookstore/categories[@code=\'1\']' ] as Set
@@ -270,7 +270,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'a new (categories) data nodes'
def json = '{"categories": [ {"code":"new1"} ] }'
and: 'the new list element is saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
when: 'the new element is deleted'
objectUnderTest.deleteListOrListElement(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', now)
then: 'the original number of data nodes is restored'
@@ -281,7 +281,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'two new (categories) data nodes in a single batch'
def json = '{"categories": [ {"code":"new1"}, {"code":"new2"} ] }'
when: 'the batches of new list element(s) are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'they can be retrieved by their xpaths'
assert objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', DIRECT_CHILDREN_ONLY).size() == 1
assert objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', DIRECT_CHILDREN_ONLY).size() == 1
@@ -298,7 +298,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'one existing and one new (categories) data nodes in a single batch'
def json = '{"categories": [ {"code":"new1"}, {"code":"1"} ] }'
when: 'the batches of new list element(s) are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'an already defined (batch) exception is thrown for the existing path'
def exceptionThrown = thrown(AlreadyDefinedException)
assert exceptionThrown.alreadyDefinedObjectNames == ['/bookstore/categories[@code=\'1\']' ] as Set
@@ -396,7 +396,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
objectUnderTest.saveData(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
when: 'the webinfo (container) is updated'
json = '{"webinfo": {"domain-name":"newdomain.com" ,"contact-email":"info@newdomain.com" }}'
- objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now)
+ objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now, ContentType.JSON)
then: 'webinfo has been updated with teh new details'
def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/webinfo', DIRECT_CHILDREN_ONLY)
result.leaves.'domain-name'[0] == 'newdomain.com'
@@ -408,7 +408,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
def 'Update bookstore top-level container data node.'() {
when: 'the bookstore top-level container is updated'
def json = '{ "bookstore": { "bookstore-name": "new bookstore" }}'
- objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/', json, now)
+ objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/', json, now, ContentType.JSON)
then: 'bookstore name has been updated'
def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', DIRECT_CHILDREN_ONLY)
result.leaves.'bookstore-name'[0] == 'new bookstore'
@@ -459,17 +459,19 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
def 'Get delta between 2 anchors'() {
when: 'attempt to get delta report between anchors'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, '/', OMIT_DESCENDANTS)
+ and: 'report is ordered based on xpath'
+ result = result.toList().sort { it.xpath }
then: 'delta report contains expected number of changes'
result.size() == 3
- and: 'delta report contains UPDATE action with expected xpath'
- assert result[0].getAction() == 'update'
+ and: 'delta report contains REPLACE action with expected xpath'
+ assert result[0].getAction() == 'replace'
assert result[0].getXpath() == '/bookstore'
+ and: 'delta report contains CREATE action with expected xpath'
+ assert result[1].getAction() == 'create'
+ assert result[1].getXpath() == "/bookstore-address[@bookstore-name='Crossword Bookstores']"
and: 'delta report contains REMOVE action with expected xpath'
- assert result[1].getAction() == 'remove'
- assert result[1].getXpath() == "/bookstore-address[@bookstore-name='Easons-1']"
- and: 'delta report contains ADD action with expected xpath'
- assert result[2].getAction() == 'add'
- assert result[2].getXpath() == "/bookstore-address[@bookstore-name='Crossword Bookstores']"
+ assert result[2].getAction() == 'remove'
+ assert result[2].getXpath() == "/bookstore-address[@bookstore-name='Easons-1']"
}
def 'Get delta between 2 anchors returns empty response when #scenario'() {
@@ -513,11 +515,11 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
'is empty' | "/bookstore/container-without-leaves"
}
- def 'Get delta between anchors for add action, where target data node #scenario'() {
+ def 'Get delta between anchors for "create" action, where target data node #scenario'() {
when: 'attempt to get delta between leaves of data nodes present in 2 anchors'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, parentNodeXpath, INCLUDE_ALL_DESCENDANTS)
then: 'the expected action is present in delta report'
- result.get(0).getAction() == 'add'
+ result.get(0).getAction() == 'create'
and: 'the expected xapth is present in delta report'
result.get(0).getXpath() == parentNodeXpath
where: 'following data was used'
@@ -531,8 +533,8 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
def 'Get delta between anchors when leaves of existing data nodes are updated,: #scenario'() {
when: 'attempt to get delta between leaves of existing data nodes'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, sourceAnchor, targetAnchor, xpath, OMIT_DESCENDANTS)
- then: 'expected action is update'
- assert result[0].getAction() == 'update'
+ then: 'expected action is "replace"'
+ assert result[0].getAction() == 'replace'
and: 'the payload has expected leaf values'
def sourceData = result[0].getSourceData()
def targetData = result[0].getTargetData()
@@ -548,8 +550,8 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
def 'Get delta between anchors when child data nodes under existing parent data nodes are updated: #scenario'() {
when: 'attempt to get delta between leaves of existing data nodes'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, sourceAnchor, targetAnchor, xpath, DIRECT_CHILDREN_ONLY)
- then: 'expected action is update'
- assert result[0].getAction() == 'update'
+ then: 'expected action is "replace"'
+ assert result[0].getAction() == 'replace'
and: 'the delta report has expected child node xpaths'
def deltaReportEntities = getDeltaReportEntities(result)
def childNodeXpathsInDeltaReport = deltaReportEntities.get('xpaths')
@@ -571,8 +573,8 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
when: 'attempt to get delta between leaves of existing data nodes'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, parentNodeXpath, INCLUDE_ALL_DESCENDANTS)
def deltaReportEntities = getDeltaReportEntities(result)
- then: 'expected action is update'
- assert result[0].getAction() == 'update'
+ then: 'expected action is "replace"'
+ assert result[0].getAction() == 'replace'
and: 'the payload has expected parent node xpath'
assert deltaReportEntities.get('xpaths').contains(parentNodeXpath)
and: 'delta report has expected source and target data'
@@ -591,14 +593,14 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
def result = objectUnderTest.getDeltaByDataspaceAnchorAndPayload(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, '/', [:], jsonPayload, OMIT_DESCENDANTS)
then: 'delta report contains expected number of changes'
result.size() == 3
- and: 'delta report contains UPDATE action with expected xpath'
- assert result[0].getAction() == 'update'
+ and: 'delta report contains "replace" action with expected xpath'
+ assert result[0].getAction() == 'replace'
assert result[0].getXpath() == '/bookstore'
- and: 'delta report contains REMOVE action with expected xpath'
+ and: 'delta report contains "remove" action with expected xpath'
assert result[1].getAction() == 'remove'
assert result[1].getXpath() == "/bookstore-address[@bookstore-name='Easons-1']"
- and: 'delta report contains ADD action with expected xpath'
- assert result[2].getAction() == 'add'
+ and: 'delta report contains "create" action with expected xpath'
+ assert result[2].getAction() == 'create'
assert result[2].getXpath() == "/bookstore-address[@bookstore-name='Crossword Bookstores']"
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy
index 0e465d84a0..9e51d80d9e 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy
@@ -134,7 +134,7 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ 'newSchema1', 'newSchema2'])
}
- def 'Create schema set error scenario: #scenario.'() {
+ def 'Attempt to create schema set, error scenario: #scenario.'() {
when: 'attempt to store schema set #schemaSetName in dataspace #dataspaceName'
populateNewYangResourcesNameToContentMapAndAllModuleReferences(0)
objectUnderTest.createSchemaSet(dataspaceName, schemaSetName, newYangResourcesNameToContentMap)
@@ -146,6 +146,14 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
'schema set already exists' | FUNCTIONAL_TEST_DATASPACE_1 | BOOKSTORE_SCHEMA_SET || AlreadyDefinedException
}
+ def 'Attempt to create duplicate schema set from modules.'() {
+ when: 'attempt to store duplicate schema set from modules'
+ objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, newYangResourcesNameToContentMap, [])
+ then: 'an Already Defined Exception is thrown'
+ thrown(AlreadyDefinedException)
+ }
+
+
/*
R E A D S C H E M A S E T I N F O U S E - C A S E S
*/
@@ -215,7 +223,8 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
when: 'all schema sets are retrieved'
def result = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1)
then: 'the result contains all expected schema sets'
- assert result.name == [ 'bookstoreSchemaSet', 'newSchema1' ]
+ assert result.name.size() == 2
+ assert result.name.containsAll('bookstoreSchemaSet', 'newSchema1')
cleanup:
objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchema1'])
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy
index fd9aa54051..5c2a4fc665 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy
@@ -271,7 +271,6 @@ class QueryServiceIntegrationSpec extends FunctionalSpecBase {
'ancestor with parent list' | '//books/ancestor::bookstore/categories' || ["/bookstore/categories[@code='1']", "/bookstore/categories[@code='2']", "/bookstore/categories[@code='3']", "/bookstore/categories[@code='4']", "/bookstore/categories[@code='5']"]
'ancestor with parent list element' | '//books/ancestor::bookstore/categories[@code="2"]' || ["/bookstore/categories[@code='2']"]
'ancestor combined with text condition' | '//books/title[text()="Matilda"]/ancestor::bookstore' || ["/bookstore"]
- 'ancestor same as target type' | '//books/title[text()="Matilda"]/ancestor::books' || ["/bookstore/categories[@code='1']/books[@title='Matilda']"]
}
def 'Cps Path query across anchors with #scenario descendants.'() {
@@ -383,7 +382,7 @@ class QueryServiceIntegrationSpec extends FunctionalSpecBase {
def result = objectUnderTest.queryDataNodesAcrossAnchors(FUNCTIONAL_TEST_DATASPACE_1, '/bookstore', OMIT_DESCENDANTS, new PaginationOption(pageIndex, pageSize))
then: 'correct bookstore names are queried'
def bookstoreNames = result.collect { it.getLeaves().get('bookstore-name') }
- assert bookstoreNames.toList() == expectedBookstoreNames
+ assert bookstoreNames.toSet() == expectedBookstoreNames.toSet()
and: 'the correct number of page size is returned'
assert result.size() == expectedPageSize
and: 'the queried nodes have expected anchor names'
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy
index 428d5f9014..ad153d6a4a 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy
@@ -21,8 +21,8 @@
package org.onap.cps.integration.functional.cps
import org.onap.cps.integration.base.FunctionalSpecBase
+import org.onap.cps.ri.utils.SessionManager
import org.onap.cps.spi.exceptions.SessionManagerException
-import org.onap.cps.spi.utils.SessionManager
class SessionManagerIntegrationSpec extends FunctionalSpecBase {
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy
new file mode 100644
index 0000000000..222b3c0f6f
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy
@@ -0,0 +1,54 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+
+class AlternateIdSpec extends CpsIntegrationSpecBase {
+
+ def setup() {
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'alternateId')
+ }
+
+ def cleanup() {
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ }
+
+ def 'AlternateId in pass-through data operations should return OK status.'() {
+ given: 'the URL for the pass-through data request'
+ def url = '/ncmp/v1/ch/alternateId/data/ds/ncmp-datastore:passthrough-running'
+ when: 'a pass-through data request is sent to NCMP'
+ def response = mvc.perform(get(url)
+ .queryParam('resourceIdentifier', 'my-resource-id')
+ .contentType(MediaType.APPLICATION_JSON))
+ .andReturn().response
+ then: 'response status is Ok'
+ assert response.status == HttpStatus.OK.value()
+ }
+
+
+
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy
index c91e750d6e..99e80323c2 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy
@@ -36,12 +36,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
class BearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
def setup() {
- dmiDispatcher.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
- registerCmHandle(DMI_URL, 'ch-1', NO_MODULE_SET_TAG)
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG)
}
def cleanup() {
- deregisterCmHandle(DMI_URL, 'ch-1')
+ deregisterCmHandle(DMI1_URL, 'ch-1')
}
def 'Bearer token is passed from NCMP to DMI in pass-through data operations.'() {
@@ -54,7 +54,7 @@ class BearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
.andExpect(status().is2xxSuccessful())
then: 'DMI has received request with bearer token'
- assert dmiDispatcher.lastAuthHeaderReceived == 'Bearer some-bearer-token'
+ assert dmiDispatcher1.lastAuthHeaderReceived == 'Bearer some-bearer-token'
where: 'all HTTP operations are applied'
httpMethod << [GET, POST, PUT, PATCH, DELETE]
@@ -70,7 +70,7 @@ class BearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
.andExpect(status().is2xxSuccessful())
then: 'DMI has received request with no authorization header'
- assert dmiDispatcher.lastAuthHeaderReceived == null
+ assert dmiDispatcher1.lastAuthHeaderReceived == null
where: 'all HTTP operations are applied'
httpMethod << [GET, POST, PUT, PATCH, DELETE]
@@ -94,7 +94,7 @@ class BearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
then: 'DMI will receive the async request with bearer token'
new PollingConditions().within(3, () -> {
- assert dmiDispatcher.lastAuthHeaderReceived == 'Bearer some-bearer-token'
+ assert dmiDispatcher1.lastAuthHeaderReceived == 'Bearer some-bearer-token'
})
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy
index c9a64e0ab8..10a9f15e21 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy
@@ -24,6 +24,7 @@ import org.apache.kafka.common.TopicPartition
import org.apache.kafka.common.serialization.StringDeserializer
import org.onap.cps.integration.KafkaTestContainer
import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.NcmpResponseStatus
import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade
import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse
import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
@@ -48,14 +49,14 @@ class CmHandleCreateSpec extends CpsIntegrationSpecBase {
def 'CM Handle registration is successful.'() {
given: 'DMI will return modules when requested'
- dmiDispatcher.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
and: 'consumer subscribed to topic'
kafkaConsumer.subscribe(['ncmp-events'])
when: 'a CM-handle is registered for creation'
def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
- def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: [cmHandleToCreate])
def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
then: 'registration gives successful response'
@@ -81,16 +82,16 @@ class CmHandleCreateSpec extends CpsIntegrationSpecBase {
assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
cleanup: 'deregister CM handle'
- deregisterCmHandle(DMI_URL, 'ch-1')
+ deregisterCmHandle(DMI1_URL, 'ch-1')
}
def 'CM Handle goes to LOCKED state when DMI gives error during module sync.'() {
given: 'DMI is not available to handle requests'
- dmiDispatcher.isAvailable = false
+ dmiDispatcher1.isAvailable = false
when: 'a CM-handle is registered for creation'
def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
- def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: [cmHandleToCreate])
objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
then: 'CM-handle goes to LOCKED state with reason MODULE_SYNC_FAILED'
@@ -104,19 +105,19 @@ class CmHandleCreateSpec extends CpsIntegrationSpecBase {
assert objectUnderTest.getYangResourcesModuleReferences('ch-1').empty
cleanup: 'deregister CM handle'
- deregisterCmHandle(DMI_URL, 'ch-1')
+ deregisterCmHandle(DMI1_URL, 'ch-1')
}
def 'Create a CM-handle with existing moduleSetTag.'() {
given: 'DMI will return modules when requested'
- dmiDispatcher.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2'], 'ch-2': ['M1', 'M3']]
+ dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2'], 'ch-2': ['M1', 'M3']]
and: 'existing CM-handles cm-1 with moduleSetTag "A", and cm-2 with moduleSetTag "B"'
- registerCmHandle(DMI_URL, 'ch-1', 'A')
- registerCmHandle(DMI_URL, 'ch-2', 'B')
+ registerCmHandle(DMI1_URL, 'ch-1', 'A')
+ registerCmHandle(DMI1_URL, 'ch-2', 'B')
when: 'a CM-handle is registered for creation with moduleSetTag "B"'
def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B')
- objectUnderTest.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]))
+ objectUnderTest.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: [cmHandleToCreate]))
then: 'the CM-handle goes to READY state'
new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
@@ -130,16 +131,48 @@ class CmHandleCreateSpec extends CpsIntegrationSpecBase {
assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort()
cleanup: 'deregister CM handles'
- deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2', 'ch-3'])
+ deregisterCmHandles(DMI1_URL, ['ch-1', 'ch-2', 'ch-3'])
+ }
+
+ def 'Create CM-handles with alternate IDs.'() {
+ given: 'DMI will return modules for all CM-handles when requested'
+ dmiDispatcher1.moduleNamesPerCmHandleId = (1..7).collectEntries{ ['ch-'+it, ['M1']] }
+ and: 'an existing CM-handle with an alternate ID'
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'existing-alt-id')
+ and: 'an existing CM-handle with no alternate ID'
+ registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, NO_ALTERNATE_ID)
+
+ when: 'a batch of CM-handles is registered for creation with various alternate IDs'
+ def cmHandlesToCreate = [
+ new NcmpServiceCmHandle(cmHandleId: 'ch-3', alternateId: NO_ALTERNATE_ID),
+ new NcmpServiceCmHandle(cmHandleId: 'ch-4', alternateId: 'unique-alt-id'),
+ new NcmpServiceCmHandle(cmHandleId: 'ch-5', alternateId: 'existing-alt-id'),
+ new NcmpServiceCmHandle(cmHandleId: 'ch-6', alternateId: 'duplicate-alt-id'),
+ new NcmpServiceCmHandle(cmHandleId: 'ch-7', alternateId: 'duplicate-alt-id'),
+ ]
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: cmHandlesToCreate)
+ def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+
+ then: 'registration gives expected responses'
+ assert dmiPluginRegistrationResponse.createdCmHandles.sort { it.cmHandle } == [
+ CmHandleRegistrationResponse.createSuccessResponse('ch-3'),
+ CmHandleRegistrationResponse.createSuccessResponse('ch-4'),
+ CmHandleRegistrationResponse.createFailureResponse('ch-5', NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED),
+ CmHandleRegistrationResponse.createSuccessResponse('ch-6'),
+ CmHandleRegistrationResponse.createFailureResponse('ch-7', NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED),
+ ]
+
+ cleanup: 'deregister CM handles'
+ deregisterCmHandles(DMI1_URL, (1..7).collect{ 'ch-'+it })
}
def 'CM Handle retry after failed module sync.'() {
given: 'DMI is not initially available to handle requests'
- dmiDispatcher.isAvailable = false
+ dmiDispatcher1.isAvailable = false
when: 'CM-handles are registered for creation'
- def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1')]
- def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: cmHandlesToCreate)
+ def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1'), new NcmpServiceCmHandle(cmHandleId: 'ch-2')]
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: cmHandlesToCreate)
objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
then: 'CM-handles go to LOCKED state'
new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
@@ -147,19 +180,22 @@ class CmHandleCreateSpec extends CpsIntegrationSpecBase {
})
when: 'DMI is available for retry'
- dmiDispatcher.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2']]
- dmiDispatcher.isAvailable = true
- and: 'the LOCKED CM handle retry time elapses (actually just subtract 3 minutes from handles lastUpdateTime)'
- overrideCmHandleLastUpdateTime('ch-1', OffsetDateTime.now().minusMinutes(3))
+ dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2'], 'ch-2': ['M1', 'M2']]
+ dmiDispatcher1.isAvailable = true
- then: 'CM-handle goes to READY state'
+ then: 'Both CM-handles go to READY state'
new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
- assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.READY
+ ['ch-1', 'ch-2'].each { cmHandleId ->
+ assert objectUnderTest.getCmHandleCompositeState(cmHandleId).cmHandleState == CmHandleState.READY
+ }
})
- and: 'CM-handle has expected modules'
- assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
- cleanup: 'deregister CM handle'
- deregisterCmHandle(DMI_URL, 'ch-1')
+ and: 'Both CM-handles have expected modules'
+ ['ch-1', 'ch-2'].each { cmHandleId ->
+ assert objectUnderTest.getYangResourcesModuleReferences(cmHandleId).moduleName.sort() == ['M1', 'M2']
+ }
+
+ cleanup: 'deregister CM handles'
+ deregisterCmHandles(DMI1_URL, ['ch-1', 'ch-2'])
}
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpdateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpdateSpec.groovy
new file mode 100644
index 0000000000..2d1588ecf9
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpdateSpec.groovy
@@ -0,0 +1,89 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.NcmpResponseStatus
+import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade
+import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse
+import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
+import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
+
+class CmHandleUpdateSpec extends CpsIntegrationSpecBase {
+
+ NetworkCmProxyInventoryFacade objectUnderTest
+
+ def setup() {
+ objectUnderTest = networkCmProxyInventoryFacade
+ }
+
+ def 'Update of CM-handle with new or unchanged alternate ID succeeds.'() {
+ given: 'DMI will return modules when requested'
+ dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2']]
+ and: "existing CM-handle with alternate ID: $oldAlternateId"
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, oldAlternateId)
+
+ when: "CM-handle is registered for update with new alternate ID: $newAlternateId"
+ def cmHandleToUpdate = new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: newAlternateId)
+ def dmiPluginRegistrationResponse =
+ objectUnderTest.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI1_URL, updatedCmHandles: [cmHandleToUpdate]))
+
+ then: 'registration gives successful response'
+ assert dmiPluginRegistrationResponse.updatedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse('ch-1')]
+
+ and: 'the CM-handle has expected alternate ID'
+ assert objectUnderTest.getNcmpServiceCmHandle('ch-1').alternateId == expectedAlternateId
+
+ cleanup: 'deregister CM handles'
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+
+ where:
+ oldAlternateId | newAlternateId || expectedAlternateId
+ '' | '' || ''
+ '' | 'new' || 'new'
+ 'old' | 'old' || 'old'
+ 'old' | null || 'old'
+ 'old' | '' || 'old'
+ 'old' | ' ' || 'old'
+ }
+
+ def 'Update of CM-handle with previously set alternate ID fails.'() {
+ given: 'DMI will return modules when requested'
+ dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2']]
+ and: 'existing CM-handle with alternate ID'
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'original')
+
+ when: 'a CM-handle is registered for update with new alternate ID'
+ def cmHandleToUpdate = new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: 'new')
+ def dmiPluginRegistrationResponse =
+ objectUnderTest.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI1_URL, updatedCmHandles: [cmHandleToUpdate]))
+
+ then: 'registration gives failure response, due to alternate ID being already associated'
+ assert dmiPluginRegistrationResponse.updatedCmHandles == [CmHandleRegistrationResponse.createFailureResponse('ch-1', NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED)]
+
+ and: 'the CM-handle still has the old alternate ID'
+ assert objectUnderTest.getNcmpServiceCmHandle('ch-1').alternateId == 'original'
+
+ cleanup: 'deregister CM handles'
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ }
+
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpgradeSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpgradeSpec.groovy
index 35ea0793ce..f93f58ce20 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpgradeSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpgradeSpec.groovy
@@ -42,14 +42,14 @@ class CmHandleUpgradeSpec extends CpsIntegrationSpecBase {
def 'Upgrade CM-handle with new moduleSetTag or no moduleSetTag.'() {
given: 'a CM-handle is created with expected initial modules: M1 and M2'
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
- registerCmHandle(DMI_URL, CM_HANDLE_ID, initialModuleSetTag)
+ dmiDispatcher1.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, CM_HANDLE_ID, initialModuleSetTag)
assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
when: "the CM-handle is upgraded with given moduleSetTag '${updatedModuleSetTag}'"
def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: updatedModuleSetTag)
def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule(
- new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade))
+ new DmiPluginRegistration(dmiPlugin: DMI1_URL, upgradedCmHandles: cmHandlesToUpgrade))
then: 'registration gives successful response'
assert dmiPluginRegistrationResponse.upgradedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(CM_HANDLE_ID)]
@@ -61,7 +61,7 @@ class CmHandleUpgradeSpec extends CpsIntegrationSpecBase {
assert cmHandleCompositeState.lockReason.details == "Upgrade to ModuleSetTag: ${updatedModuleSetTag}"
when: 'DMI will return different modules for upgrade: M1 and M3'
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M3']
+ dmiDispatcher1.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M3']
then: 'CM-handle goes to READY state'
new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
@@ -75,7 +75,7 @@ class CmHandleUpgradeSpec extends CpsIntegrationSpecBase {
assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
cleanup: 'deregister CM-handle'
- deregisterCmHandle(DMI_URL, CM_HANDLE_ID)
+ deregisterCmHandle(DMI1_URL, CM_HANDLE_ID)
where:
initialModuleSetTag | updatedModuleSetTag
@@ -87,19 +87,19 @@ class CmHandleUpgradeSpec extends CpsIntegrationSpecBase {
def 'Upgrade CM-handle with existing moduleSetTag.'() {
given: 'DMI will return modules for registration'
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG] = ['M1', 'M3']
+ dmiDispatcher1.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
+ dmiDispatcher1.moduleNamesPerCmHandleId[CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG] = ['M1', 'M3']
and: "an existing CM-handle handle with moduleSetTag '${updatedModuleSetTag}'"
- registerCmHandle(DMI_URL, CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG, updatedModuleSetTag)
+ registerCmHandle(DMI1_URL, CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG, updatedModuleSetTag)
assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG).moduleName.sort()
and: "a CM-handle with moduleSetTag '${initialModuleSetTag}' which will be upgraded"
- registerCmHandle(DMI_URL, CM_HANDLE_ID, initialModuleSetTag)
+ registerCmHandle(DMI1_URL, CM_HANDLE_ID, initialModuleSetTag)
assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
when: "CM-handle is upgraded to moduleSetTag '${updatedModuleSetTag}'"
def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: updatedModuleSetTag)
def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule(
- new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade))
+ new DmiPluginRegistration(dmiPlugin: DMI1_URL, upgradedCmHandles: cmHandlesToUpgrade))
then: 'registration gives successful response'
assert dmiPluginRegistrationResponse.upgradedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(CM_HANDLE_ID)]
@@ -116,7 +116,7 @@ class CmHandleUpgradeSpec extends CpsIntegrationSpecBase {
assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
cleanup: 'deregister CM-handle'
- deregisterCmHandles(DMI_URL, [CM_HANDLE_ID, CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG])
+ deregisterCmHandles(DMI1_URL, [CM_HANDLE_ID, CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG])
where:
initialModuleSetTag | updatedModuleSetTag
@@ -126,14 +126,14 @@ class CmHandleUpgradeSpec extends CpsIntegrationSpecBase {
def 'Skip upgrade of CM-handle with same moduleSetTag as before.'() {
given: 'an existing CM-handle with expected initial modules: M1 and M2'
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
- registerCmHandle(DMI_URL, CM_HANDLE_ID, 'same')
+ dmiDispatcher1.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, CM_HANDLE_ID, 'same')
assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
when: 'CM-handle is upgraded with the same moduleSetTag'
def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'same')
objectUnderTest.updateDmiRegistrationAndSyncModule(
- new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade))
+ new DmiPluginRegistration(dmiPlugin: DMI1_URL, upgradedCmHandles: cmHandlesToUpgrade))
then: 'CM-handle remains in READY state'
assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState
@@ -145,20 +145,20 @@ class CmHandleUpgradeSpec extends CpsIntegrationSpecBase {
assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
cleanup: 'deregister CM-handle'
- deregisterCmHandle(DMI_URL, CM_HANDLE_ID)
+ deregisterCmHandle(DMI1_URL, CM_HANDLE_ID)
}
def 'Upgrade of CM-handle fails due to DMI error.'() {
given: 'a CM-handle exists'
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
- registerCmHandle(DMI_URL, CM_HANDLE_ID, 'oldTag')
+ dmiDispatcher1.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, CM_HANDLE_ID, 'oldTag')
and: 'DMI is not available for upgrade'
- dmiDispatcher.isAvailable = false
+ dmiDispatcher1.isAvailable = false
when: 'the CM-handle is upgraded'
def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'newTag')
objectUnderTest.updateDmiRegistrationAndSyncModule(
- new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade))
+ new DmiPluginRegistration(dmiPlugin: DMI1_URL, upgradedCmHandles: cmHandlesToUpgrade))
then: 'CM-handle goes to LOCKED state with reason MODULE_UPGRADE_FAILED'
new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
@@ -171,7 +171,7 @@ class CmHandleUpgradeSpec extends CpsIntegrationSpecBase {
assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == 'oldTag'
cleanup: 'deregister CM-handle'
- deregisterCmHandle(DMI_URL, CM_HANDLE_ID)
+ deregisterCmHandle(DMI1_URL, CM_HANDLE_ID)
}
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobResultServiceSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobResultServiceSpec.groovy
new file mode 100644
index 0000000000..4d04eeeb81
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobResultServiceSpec.groovy
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.datajobs.DataJobResultService
+import org.springframework.beans.factory.annotation.Autowired
+
+class DataJobResultServiceSpec extends CpsIntegrationSpecBase {
+
+ @Autowired
+ DataJobResultService dataJobResultService;
+
+ def 'Get the status of a data job from DMI.'() {
+ given: 'the required data about the data job'
+ def authorization = 'my authorization header'
+ def dmiServiceName = DMI1_URL
+ def dataProducerId = 'some-data-producer-id'
+ def dataProducerJobId = 'some-data-producer-job-id'
+ def destination = 'some-destination'
+ when: 'the data job status checked'
+ def result = dataJobResultService.getDataJobResult(authorization, dmiServiceName, dataProducerId, dataProducerJobId, destination)
+ then: 'the status is that defined in the mock service.'
+ assert result == '{ "result": "some result"}'
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy
new file mode 100644
index 0000000000..6e5c0e40c2
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy
@@ -0,0 +1,23 @@
+package org.onap.cps.integration.functional.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.datajobs.DataJobStatusService
+import org.springframework.beans.factory.annotation.Autowired
+
+class DataJobStatusServiceSpec extends CpsIntegrationSpecBase {
+
+ @Autowired
+ DataJobStatusService dataJobStatusService
+
+ def 'Get the status of a data job from DMI.'() {
+ given: 'the required data about the data job'
+ def dmiServiceName = DMI1_URL
+ def dataProducerId = 'some-data-producer-id'
+ def dataProducerJobId = 'some-data-producer-job-id'
+ def authorization = 'my authorization header'
+ when: 'the data job status checked'
+ def result = dataJobStatusService.getDataJobStatus(authorization, dmiServiceName, dataProducerId, dataProducerJobId)
+ then: 'the status is that defined in the mock service.'
+ assert result == 'status details from mock service'
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy
new file mode 100644
index 0000000000..4e9b809eff
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy
@@ -0,0 +1,65 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.springframework.http.MediaType
+
+import static org.springframework.http.HttpMethod.DELETE
+import static org.springframework.http.HttpMethod.GET
+import static org.springframework.http.HttpMethod.PATCH
+import static org.springframework.http.HttpMethod.POST
+import static org.springframework.http.HttpMethod.PUT
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
+
+class DmiUrlEncodingPassthroughSpec extends CpsIntegrationSpecBase {
+
+ def setup() {
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG)
+ }
+
+ def cleanup() {
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ }
+
+ def 'DMI URL encoding for pass-through operational data operations with GET request'() {
+ when: 'sending a GET pass-through data request to NCMP'
+ mvc.perform(request(GET, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-operational')
+ .queryParam('resourceIdentifier', 'parent/child')
+ .queryParam('options', '(a=1,b=2)'))
+ .andExpect(status().is2xxSuccessful())
+ then: 'verify that DMI received the request with the correctly encoded URL'
+ assert dmiDispatcher1.dmiResourceDataUrl == '/dmi/v1/ch/ch-1/data/ds/ncmp-datastore%3Apassthrough-operational?resourceIdentifier=parent%2Fchild&options=%28a%3D1%2Cb%3D2%29'
+ }
+
+ def 'DMI URL encoding for pass-through running data operations with POST request'() {
+ when: 'sending a pass-through data request to NCMP with various HTTP methods'
+ mvc.perform(request(POST, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running')
+ .queryParam('resourceIdentifier', 'parent/child')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content('{ "some-json": "data" }'))
+ .andExpect(status().is2xxSuccessful())
+ then: 'verify that DMI received the request with the correctly encoded URL'
+ assert dmiDispatcher1.dmiResourceDataUrl == '/dmi/v1/ch/ch-1/data/ds/ncmp-datastore%3Apassthrough-running?resourceIdentifier=parent%2Fchild'
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy
new file mode 100644
index 0000000000..56d4bfaee4
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy
@@ -0,0 +1,74 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional.ncmp
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.springframework.http.HttpHeaders
+import org.springframework.http.MediaType
+
+import static org.springframework.http.HttpMethod.POST
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request
+
+class PolicyExecutorIntegrationSpec extends CpsIntegrationSpecBase {
+
+ def objectMapper = new ObjectMapper()
+
+ def setup() {
+ // Enable mocked policy executor logic
+ policyDispatcher.allowAll = false;
+ //minimum setup for cm handles with alternate ids
+ dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': [], 'ch-2': [], 'ch-3':[]]
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'fdn1')
+ registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, 'fdn2')
+ registerCmHandle(DMI1_URL, 'ch-3', NO_MODULE_SET_TAG, 'mock slow response')
+ }
+
+ def cleanup() {
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ deregisterCmHandle(DMI1_URL, 'ch-2')
+ deregisterCmHandle(DMI1_URL, 'ch-3')
+ }
+
+ def 'Policy Executor create request with #scenario.'() {
+ when: 'a pass-through write request is sent to NCMP'
+ def response = mvc.perform(request(POST, "/ncmp/v1/ch/$cmHandle/data/ds/ncmp-datastore:passthrough-running")
+ .queryParam('resourceIdentifier', 'my-resource-id')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content('{ "some-json": "data" }')
+ .header(HttpHeaders.AUTHORIZATION, authorization))
+ .andReturn().response
+ then: 'the expected status code is returned'
+ response.getStatus() == execpectedStatusCode
+ and: 'when not allowed the response body contains the expected message'
+ if (expectedMessage!='allow') {
+ def bodyAsMap = objectMapper.readValue(response.getContentAsByteArray(), Map.class)
+ assert bodyAsMap.get('message').endsWith(expectedMessage)
+ }
+ where: 'following parameters are used'
+ scenario | cmHandle | authorization || execpectedStatusCode || expectedMessage
+ 'accepted cm handle' | 'ch-1' | 'mock expects "ABC"' || 201 || 'allow'
+ 'un-accepted cm handle' | 'ch-2' | 'mock expects "ABC"' || 409 || 'deny from mock server (dispatcher)'
+ 'timeout' | 'ch-3' | 'mock expects "ABC"' || 409 || 'test default decision'
+ 'invalid authorization' | 'ch-1' | 'something else' || 409 || 'test default decision'
+ }
+
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/RestApiSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/RestApiSpec.groovy
index ab189c2917..1e1af556f1 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/RestApiSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/RestApiSpec.groovy
@@ -35,13 +35,13 @@ class RestApiSpec extends CpsIntegrationSpecBase {
def 'Register CM Handles using REST API.'() {
given: 'DMI will return modules'
- dmiDispatcher.moduleNamesPerCmHandleId = [
+ dmiDispatcher1.moduleNamesPerCmHandleId = [
'ch-1': ['M1', 'M2'],
'ch-2': ['M1', 'M2'],
'ch-3': ['M1', 'M3']
]
when: 'a POST request is made to register the CM Handles'
- def requestBody = '{"dmiPlugin":"'+DMI_URL+'","createdCmHandles":[{"cmHandle":"ch-1"},{"cmHandle":"ch-2"},{"cmHandle":"ch-3"}]}'
+ def requestBody = '{"dmiPlugin":"'+DMI1_URL+'","createdCmHandles":[{"cmHandle":"ch-1","alternateId":"alt-1"},{"cmHandle":"ch-2","alternateId":"alt-2"},{"cmHandle":"ch-3","alternateId":"alt-3"}]}'
mvc.perform(post('/ncmpInventory/v1/ch').contentType(MediaType.APPLICATION_JSON).content(requestBody))
.andExpect(status().is2xxSuccessful())
then: 'CM-handles go to READY state'
@@ -76,9 +76,30 @@ class RestApiSpec extends CpsIntegrationSpecBase {
'M3' || ['ch-3']
}
+ def 'Search for CM Handles using Cps Path Query.'() {
+ given: 'a JSON request body containing search parameter'
+ def requestBodyWithSearchCondition = """{
+ "cmHandleQueryParameters": [
+ {
+ "conditionName": "cmHandleWithCpsPath",
+ "conditionParameters": [ {"cpsPath" : "%s"} ]
+ }
+ ]
+ }""".formatted(cpsPath)
+ expect: "a search for cps path ${cpsPath} returns expected CM handles"
+ mvc.perform(post('/ncmp/v1/ch/id-searches').contentType(MediaType.APPLICATION_JSON).content(requestBodyWithSearchCondition))
+ .andExpect(status().is2xxSuccessful())
+ .andExpect(jsonPath('$[*]', containsInAnyOrder(expectedCmHandles.toArray())))
+ .andExpect(jsonPath('$', hasSize(expectedCmHandles.size())));
+ where:
+ scenario | cpsPath || expectedCmHandles
+ 'All Ready CM handles' | "//state[@cm-handle-state='READY']" || ['ch-1', 'ch-2', 'ch-3']
+ 'Having Alternate ID alt-3' | "//cm-handles[@alternate-id='alt-3']" || ['ch-3']
+ }
+
def 'De-register CM handles using REST API.'() {
when: 'a POST request is made to deregister the CM Handle'
- def requestBody = '{"dmiPlugin":"'+DMI_URL+'", "removedCmHandles": ["ch-1", "ch-2", "ch-3"]}'
+ def requestBody = '{"dmiPlugin":"'+DMI1_URL+'", "removedCmHandles": ["ch-1", "ch-2", "ch-3"]}'
mvc.perform(post('/ncmpInventory/v1/ch').contentType(MediaType.APPLICATION_JSON).content(requestBody))
.andExpect(status().is2xxSuccessful())
then: 'the CM handles are not found using GET'
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/WriteSubJobSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/WriteSubJobSpec.groovy
new file mode 100644
index 0000000000..834e1399e3
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/WriteSubJobSpec.groovy
@@ -0,0 +1,75 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.datajobs.DataJobService
+import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata
+import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest
+import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse
+import org.onap.cps.ncmp.api.datajobs.models.WriteOperation
+import org.springframework.beans.factory.annotation.Autowired
+
+class WriteSubJobSpec extends CpsIntegrationSpecBase {
+
+ @Autowired
+ DataJobService dataJobService
+
+ def setup() {
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1']
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-2'] = ['M2']
+ dmiDispatcher2.moduleNamesPerCmHandleId['ch-3'] = ['M3']
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'p1')
+ registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, 'p2')
+ registerCmHandle(DMI2_URL, 'ch-3', NO_MODULE_SET_TAG, 'p3')
+ }
+
+ def cleanup() {
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ deregisterCmHandle(DMI1_URL, 'ch-2')
+ deregisterCmHandle(DMI2_URL, 'ch-3')
+ }
+
+ def 'Create a sub-job write request.'() {
+ given: 'the required input data for the write job'
+ def authorization = 'my authorization header'
+ def dataJobWriteRequest = new DataJobWriteRequest([new WriteOperation('p1', '', '', null), new WriteOperation('p2', '', '', null), new WriteOperation('p3', '', '', null)])
+ def myDataJobMetadata = new DataJobMetadata('d1', '', '')
+ def dataJobId = 'my-data-job-id'
+ when: 'sending a write job to NCMP with 2 sub-jobs for DMI 1 and 1 sub-job for DMI 2'
+ def response = dataJobService.writeDataJob(authorization, dataJobId, myDataJobMetadata, dataJobWriteRequest)
+ then: 'each DMI received the expected sub-jobs and the response has the expected values'
+ assert response.size() == 2
+ assert response[0].class == SubJobWriteResponse.class
+ assert response[0].subJobId == "some sub job id"
+ assert response[0].dmiServiceName == "some dmi service name"
+ assert response[0].dataProducerId == "some data producer id"
+ and: 'dmi 1 received the correct job details'
+ def receivedSubJobsForDispatcher1 = dmiDispatcher1.receivedSubJobs['?destination=d1']['data'].collect()
+ assert receivedSubJobsForDispatcher1.size() == 2
+ assert receivedSubJobsForDispatcher1[0]['path'] == 'p1'
+ assert receivedSubJobsForDispatcher1[1]['path'] == 'p2'
+ and: 'dmi 2 received the correct job details'
+ def receivedSubJobsForDispatcher2 = dmiDispatcher2.receivedSubJobs['?destination=d1']['data'].collect()
+ assert receivedSubJobsForDispatcher2.size() == 1
+ assert receivedSubJobsForDispatcher2[0]['path'] == 'p3'
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy
index 0ca200211a..f6ae27d129 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2023-2024 Nordix Foundation
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
@@ -22,6 +23,7 @@ package org.onap.cps.integration.performance.base
import org.onap.cps.integration.ResourceMeter
import org.onap.cps.spi.FetchDescendantsOption
+import org.onap.cps.utils.ContentType
import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME
import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
@@ -79,7 +81,7 @@ class NcmpPerfTestBase extends PerfTestBase {
def batchSize = 100
for (def i = 0; i < TOTAL_CM_HANDLES; i += batchSize) {
def data = '{ "cm-handles": [' + (1..batchSize).collect { innerNodeJsonTemplate.replace('CMHANDLE_ID_HERE', (it + i).toString()) }.join(',') + ']}'
- cpsDataService.saveListElements(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, '/dmi-registry', data, now)
+ cpsDataService.saveListElements(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, '/dmi-registry', data, now, ContentType.JSON)
}
}
@@ -91,7 +93,7 @@ class NcmpPerfTestBase extends PerfTestBase {
innerNodeJsonTemplate.replace('CM_HANDLE_ID_HERE', (it + i).toString())
.replace('ALTERNATE_ID_AS_PATH', (it + i).toString())
}.join(',') + ']}'
- cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry', data, now)
+ cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry', data, now, ContentType.JSON)
}
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy
index 7bcec968e5..03abdb4b3f 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy
@@ -50,7 +50,7 @@ class UpdatePerfTest extends CpsPerfTestBase {
given: 'replacement JSON for node containing list of device nodes'
def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}'
when: 'the container node is updated'
- objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now)
+ objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now, ContentType.JSON)
then: 'there are the expected number of total nodes'
assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
where:
@@ -68,7 +68,7 @@ class UpdatePerfTest extends CpsPerfTestBase {
def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}'
when: 'the container node is updated'
resourceMeter.start()
- objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now)
+ objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now, ContentType.JSON)
resourceMeter.stop()
then: 'there are the expected number of total nodes'
assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
index 0195611740..9f6c78d5f5 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2023-2024 Nordix Foundation
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
@@ -20,6 +21,8 @@
package org.onap.cps.integration.performance.cps
+import org.onap.cps.utils.ContentType
+
import java.time.OffsetDateTime
import org.onap.cps.integration.performance.base.CpsPerfTestBase
@@ -87,7 +90,7 @@ class WritePerfTest extends CpsPerfTestBase {
']}'
when: 'device nodes are added'
resourceMeter.start()
- cpsDataService.saveListElements(CPS_PERFORMANCE_TEST_DATASPACE, WRITE_TEST_ANCHOR, '/openroadm-devices', jsonListData, OffsetDateTime.now())
+ cpsDataService.saveListElements(CPS_PERFORMANCE_TEST_DATASPACE, WRITE_TEST_ANCHOR, '/openroadm-devices', jsonListData, OffsetDateTime.now(), ContentType.JSON)
resourceMeter.stop()
then: 'the operation takes less than #expectedDuration and memory used is within limit'
recordAndAssertResourceUsage("Saving list of ${totalNodes} devices",
diff --git a/integration-test/src/test/java/org/onap/cps/integration/DmiStubTestContainer.java b/integration-test/src/test/java/org/onap/cps/integration/DmiStubTestContainer.java
new file mode 100644
index 0000000000..1bffb35c14
--- /dev/null
+++ b/integration-test/src/test/java/org/onap/cps/integration/DmiStubTestContainer.java
@@ -0,0 +1,61 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration;
+
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.utility.DockerImageName;
+
+public class DmiStubTestContainer extends GenericContainer<DmiStubTestContainer> {
+
+ public static final String IMAGE_NAME_AND_VERSION =
+ "nexus3.onap.org:10003/onap/dmi-plugin-demo-and-csit-stub:latest";
+ public static final String DMI_STUB_URL = "http://localhost:8784";
+
+ private static DmiStubTestContainer dmiStubTestContainer;
+
+ private DmiStubTestContainer() {
+ super(DockerImageName.parse(IMAGE_NAME_AND_VERSION));
+ }
+
+ /**
+ * Provides an instance of the Dmi Plugin Stub test container wrapper.
+ * This will allow to interact with the DMI Stub in our acceptance tests.
+ *
+ * @return DmiStubTestContainer
+ */
+ public static DmiStubTestContainer getInstance() {
+ if (dmiStubTestContainer == null) {
+ dmiStubTestContainer = new DmiStubTestContainer();
+ dmiStubTestContainer.addFixedExposedPort(8784, 8092);
+ Runtime.getRuntime().addShutdownHook(new Thread(dmiStubTestContainer::close));
+ }
+ return dmiStubTestContainer;
+ }
+
+ @Override
+ public void start() {
+ super.start();
+ }
+
+ @Override
+ public void stop() {
+ // Method intentionally left blank
+ }
+}
diff --git a/integration-test/src/test/resources/application.yml b/integration-test/src/test/resources/application.yml
index fefae345e6..853797aac5 100644
--- a/integration-test/src/test/resources/application.yml
+++ b/integration-test/src/test/resources/application.yml
@@ -180,8 +180,6 @@ ncmp:
timers:
advised-modules-sync:
sleep-time-ms: 1000
- locked-modules-sync:
- sleep-time-ms: 1000
cm-handle-data-sync:
sleep-time-ms: 30000
subscription-forwarding:
@@ -213,8 +211,24 @@ ncmp:
init:
mode: ALWAYS
+ policy-executor:
+ enabled: true
+ defaultDecision: "test default decision"
+ server:
+ address: http://localhost
+ port: 8790
+ httpclient:
+ all-services:
+ maximumInMemorySizeInMegabytes: 1
+ maximumConnectionsTotal: 10
+ pendingAcquireMaxCount: 10
+ connectionTimeoutInSeconds: 30
+ readTimeoutInSeconds: 1
+ writeTimeoutInSeconds: 30
+
hazelcast:
cluster-name: cps-and-ncmp-test-caches
+ instance-config-name: "cps-and-ncmp-hazelcast-instance-test-config"
mode:
kubernetes:
enabled: false
diff --git a/jacoco-report/pom.xml b/jacoco-report/pom.xml
index 2610ffaacc..1f43153977 100644
--- a/jacoco-report/pom.xml
+++ b/jacoco-report/pom.xml
@@ -5,7 +5,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/k6-tests/README.md b/k6-tests/README.md
index 0fdebcfe9d..9a385e100a 100644
--- a/k6-tests/README.md
+++ b/k6-tests/README.md
@@ -4,8 +4,7 @@
k6 tests are written in JavaScript.
## k6 installation
-Follow the instructions in the [k6 installation guide](https://grafana.com/docs/k6/latest/set-up/install-k6/)
-to get started.
+Follow the instructions in the [build from source guide](https://github.com/mostafa/xk6-kafka) to get started.
## Running the k6 test suites
Simply run the main script. (The script assumes k6 and docker-compose have been installed).
diff --git a/k6-tests/install-deps.sh b/k6-tests/install-deps.sh
new file mode 100755
index 0000000000..bb5deb93dd
--- /dev/null
+++ b/k6-tests/install-deps.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# Copyright 2024 Nordix Foundation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+echo "---> install-deps.sh"
+echo "Installing dependencies"
+
+# Create directory for downloaded binaries.
+mkdir -p bin
+touch bin/.gitignore
+
+# Add it to the PATH, so downloaded versions will be used.
+export PATH="$(pwd)/bin:$PATH"
+
+# Download docker-compose.
+if [ ! -x bin/docker-compose ]; then
+ echo " Downloading docker-compose"
+ curl -s -L https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64 > bin/docker-compose
+ chmod +x bin/docker-compose
+else
+ echo " docker-compose already installed"
+fi
+docker-compose version
+
+# Download k6 with kafka extension.
+if [ ! -x bin/k6 ]; then
+ echo " Downloading k6 with kafka extension"
+ curl -s -L https://github.com/mostafa/xk6-kafka/releases/download/v0.26.0/xk6-kafka_v0.26.0_linux_amd64.tar.gz | tar -xz
+ mv dist/xk6-kafka_v0.26.0_linux_amd64 bin/k6 && rmdir dist
+ chmod +x bin/k6
+else
+ echo " k6 already installed"
+fi
+k6 --version
diff --git a/k6-tests/ncmp/common/cmhandle-crud.js b/k6-tests/ncmp/common/cmhandle-crud.js
index 6d5aff7fca..0fea1a44f2 100644
--- a/k6-tests/ncmp/common/cmhandle-crud.js
+++ b/k6-tests/ncmp/common/cmhandle-crud.js
@@ -18,34 +18,43 @@
* ============LICENSE_END=========================================================
*/
-import http from 'k6/http';
-import { check, sleep } from 'k6';
-import { NCMP_BASE_URL, DMI_PLUGIN_URL, TOTAL_CM_HANDLES, REGISTRATION_BATCH_SIZE, CONTENT_TYPE_JSON_PARAM, makeBatchOfCmHandleIds } from './utils.js';
+import { sleep } from 'k6';
+import { performPostRequest, NCMP_BASE_URL, DMI_PLUGIN_URL, TOTAL_CM_HANDLES, MODULE_SET_TAGS
+} from './utils.js';
import { executeCmHandleIdSearch } from './search-base.js';
-export function registerAllCmHandles() {
- forEachBatchOfCmHandles(createCmHandles);
- waitForAllCmHandlesToBeReady();
+export function createCmHandles(cmHandleIds) {
+ const url = `${NCMP_BASE_URL}/ncmpInventory/v1/ch`;
+ const payload = JSON.stringify(createCmHandlePayload(cmHandleIds));
+ return performPostRequest(url, payload, 'createCmHandles');
}
-export function deregisterAllCmHandles() {
- forEachBatchOfCmHandles(deleteCmHandles);
+export function deleteCmHandles(cmHandleIds) {
+ const url = `${NCMP_BASE_URL}/ncmpInventory/v1/ch`;
+ const payload = JSON.stringify({
+ "dmiPlugin": DMI_PLUGIN_URL,
+ "removedCmHandles": cmHandleIds,
+ });
+ return performPostRequest(url, payload, 'deleteCmHandles');
}
-function forEachBatchOfCmHandles(functionToExecute) {
- const TOTAL_BATCHES = Math.ceil(TOTAL_CM_HANDLES / REGISTRATION_BATCH_SIZE);
- for (let batchNumber = 0; batchNumber < TOTAL_BATCHES; batchNumber++) {
- const nextBatchOfCmHandleIds = makeBatchOfCmHandleIds(REGISTRATION_BATCH_SIZE, batchNumber);
- functionToExecute(nextBatchOfCmHandleIds);
- }
+export function waitForAllCmHandlesToBeReady() {
+ const POLLING_INTERVAL_SECONDS = 5;
+ let cmHandlesReady = 0;
+ do {
+ sleep(POLLING_INTERVAL_SECONDS);
+ cmHandlesReady = getNumberOfReadyCmHandles();
+ console.log(`${cmHandlesReady}/${TOTAL_CM_HANDLES} CM handles are READY`);
+ } while (cmHandlesReady < TOTAL_CM_HANDLES);
}
-function createCmHandles(cmHandleIds) {
- const url = `${NCMP_BASE_URL}/ncmpInventory/v1/ch`;
- const payload = {
+function createCmHandlePayload(cmHandleIds) {
+ return {
"dmiPlugin": DMI_PLUGIN_URL,
- "createdCmHandles": cmHandleIds.map(cmHandleId => ({
+ "createdCmHandles": cmHandleIds.map((cmHandleId, index) => ({
"cmHandle": cmHandleId,
+ "alternateId": cmHandleId.replace('ch-', 'alt-'),
+ "moduleSetTag": MODULE_SET_TAGS[index % MODULE_SET_TAGS.length],
"cmHandleProperties": {"neType": "RadioNode"},
"publicCmHandleProperties": {
"Color": "yellow",
@@ -54,34 +63,10 @@ function createCmHandles(cmHandleIds) {
}
})),
};
- const response = http.post(url, JSON.stringify(payload), CONTENT_TYPE_JSON_PARAM);
- check(response, { 'create CM-handles status equals 200': (r) => r.status === 200 });
- return response;
-}
-
-function deleteCmHandles(cmHandleIds) {
- const url = `${NCMP_BASE_URL}/ncmpInventory/v1/ch`;
- const payload = {
- "dmiPlugin": DMI_PLUGIN_URL,
- "removedCmHandles": cmHandleIds,
- };
- const response = http.post(url, JSON.stringify(payload), CONTENT_TYPE_JSON_PARAM);
- check(response, { 'delete CM-handles status equals 200': (r) => r.status === 200 });
- return response;
-}
-
-function waitForAllCmHandlesToBeReady() {
- const POLLING_INTERVAL_SECONDS = 5;
- let cmHandlesReady = 0;
- do {
- sleep(POLLING_INTERVAL_SECONDS);
- cmHandlesReady = getNumberOfReadyCmHandles();
- console.log(`${cmHandlesReady}/${TOTAL_CM_HANDLES} CM handles are READY`);
- } while (cmHandlesReady < TOTAL_CM_HANDLES);
}
function getNumberOfReadyCmHandles() {
- const response = executeCmHandleIdSearch('readyCmHandles');
+ const response = executeCmHandleIdSearch('cps-path-for-ready-cm-handles');
const arrayOfCmHandleIds = JSON.parse(response.body);
return arrayOfCmHandleIds.length;
}
diff --git a/k6-tests/ncmp/common/passthrough-crud.js b/k6-tests/ncmp/common/passthrough-crud.js
new file mode 100644
index 0000000000..86fcef6242
--- /dev/null
+++ b/k6-tests/ncmp/common/passthrough-crud.js
@@ -0,0 +1,74 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
+import {
+ performPostRequest,
+ performGetRequest,
+ NCMP_BASE_URL,
+ LEGACY_BATCH_TOPIC_NAME,
+ TOTAL_CM_HANDLES,
+} from './utils.js';
+
+export function passthroughRead(useAlternateId) {
+ const cmHandleReference = getRandomCmHandleReference(useAlternateId);
+ const resourceIdentifier = 'my-resource-identifier';
+ const datastoreName = 'ncmp-datastore:passthrough-operational';
+ const includeDescendants = true;
+ const url = generatePassthroughUrl(cmHandleReference, datastoreName, resourceIdentifier, includeDescendants);
+ return performGetRequest(url, 'passthroughRead');
+}
+
+export function passthroughWrite(useAlternateId) {
+ const cmHandleReference = getRandomCmHandleReference(useAlternateId);
+ const resourceIdentifier = 'my-resource-identifier';
+ const datastoreName = 'ncmp-datastore:passthrough-running';
+ const includeDescendants = false;
+ const url = generatePassthroughUrl(cmHandleReference, datastoreName, resourceIdentifier, includeDescendants);
+ const payload = JSON.stringify({"neType": "BaseStation"});
+ return performPostRequest(url, payload, 'passthroughWrite');
+}
+
+export function legacyBatchRead(cmHandleIds) {
+ const url = `${NCMP_BASE_URL}/ncmp/v1/data?topic=${LEGACY_BATCH_TOPIC_NAME}`
+ const payload = JSON.stringify({
+ "operations": [
+ {
+ "resourceIdentifier": "parent/child",
+ "targetIds": cmHandleIds,
+ "datastore": "ncmp-datastore:passthrough-operational",
+ "options": "(fields=schemas/schema)",
+ "operationId": "12",
+ "operation": "read"
+ }
+ ]
+ });
+ return performPostRequest(url, payload, 'batchRead');
+}
+
+function getRandomCmHandleReference(useAlternateId) {
+ const prefix = useAlternateId ? 'alt' : 'ch';
+ return `${prefix}-${randomIntBetween(1, TOTAL_CM_HANDLES)}`;
+}
+
+function generatePassthroughUrl(cmHandleReference, datastoreName, resourceIdentifier, includeDescendants) {
+ const descendantsParam = includeDescendants ? `&include-descendants=${includeDescendants}` : '';
+ return `${NCMP_BASE_URL}/ncmp/v1/ch/${cmHandleReference}/data/ds/${datastoreName}?resourceIdentifier=${resourceIdentifier}${descendantsParam}`;
+} \ No newline at end of file
diff --git a/k6-tests/ncmp/common/search-base.js b/k6-tests/ncmp/common/search-base.js
index bc964856af..1e98e9b07b 100644
--- a/k6-tests/ncmp/common/search-base.js
+++ b/k6-tests/ncmp/common/search-base.js
@@ -18,27 +18,7 @@
* ============LICENSE_END=========================================================
*/
-import http from 'k6/http';
-import { NCMP_BASE_URL, CONTENT_TYPE_JSON_PARAM } from './utils.js';
-
-const SEARCH_PARAMETERS_PER_SCENARIO = {
- 'module': {
- 'cmHandleQueryParameters': [
- {
- 'conditionName': 'hasAllModules',
- 'conditionParameters': [{'moduleName': 'ietf-yang-types-1'}]
- }
- ]
- },
- 'readyCmHandles': {
- 'cmHandleQueryParameters': [
- {
- 'conditionName': 'cmHandleWithCpsPath',
- 'conditionParameters': [{'cpsPath': '//state[@cm-handle-state="READY"]'}]
- }
- ]
- }
-};
+import {performPostRequest, NCMP_BASE_URL} from './utils.js';
export function executeCmHandleSearch(scenario) {
return executeSearchRequest('searches', scenario);
@@ -52,6 +32,43 @@ function executeSearchRequest(searchType, scenario) {
const searchParameters = SEARCH_PARAMETERS_PER_SCENARIO[scenario];
const payload = JSON.stringify(searchParameters);
const url = `${NCMP_BASE_URL}/ncmp/v1/ch/${searchType}`;
- const response = http.post(url, payload, CONTENT_TYPE_JSON_PARAM);
- return response;
+ return performPostRequest(url, payload, searchType);
}
+
+const SEARCH_PARAMETERS_PER_SCENARIO = {
+ "no-filter": {
+ "cmHandleQueryParameters": []
+ },
+ "module": {
+ "cmHandleQueryParameters": [
+ {
+ "conditionName": "hasAllModules",
+ "conditionParameters": [{"moduleName": "ietf-yang-types"}]
+ }
+ ]
+ },
+ "property": {
+ "cmHandleQueryParameters": [
+ {
+ "conditionName": "hasAllProperties",
+ "conditionParameters": [{"Color": "yellow"}]
+ }
+ ]
+ },
+ "trust-level": {
+ "cmHandleQueryParameters": [
+ {
+ "conditionName": "cmHandleWithTrustLevel",
+ "conditionParameters": [ {"trustLevel": "COMPLETE"} ]
+ }
+ ]
+ },
+ "cps-path-for-ready-cm-handles": {
+ "cmHandleQueryParameters": [
+ {
+ "conditionName": "cmHandleWithCpsPath",
+ "conditionParameters": [{"cpsPath": "//state[@cm-handle-state='READY']"}]
+ }
+ ]
+ }
+};
diff --git a/k6-tests/ncmp/common/utils.js b/k6-tests/ncmp/common/utils.js
index 54b4c3a099..a429675aaf 100644
--- a/k6-tests/ncmp/common/utils.js
+++ b/k6-tests/ncmp/common/utils.js
@@ -18,19 +18,20 @@
* ============LICENSE_END=========================================================
*/
+import http from 'k6/http';
export const NCMP_BASE_URL = 'http://localhost:8883';
export const DMI_PLUGIN_URL = 'http://ncmp-dmi-plugin-demo-and-csit-stub:8092';
export const TOTAL_CM_HANDLES = 20000;
export const REGISTRATION_BATCH_SIZE = 100;
-export const CONTENT_TYPE_JSON_PARAM = { headers: {'Content-Type': 'application/json'} };
+export const READ_DATA_FOR_CM_HANDLE_DELAY_MS = 300; // must have same value as in docker-compose.yml
+export const WRITE_DATA_FOR_CM_HANDLE_DELAY_MS = 670; // must have same value as in docker-compose.yml
+export const CONTENT_TYPE_JSON_PARAM = {'Content-Type': 'application/json'};
+export const LEGACY_BATCH_THROUGHPUT_TEST_BATCH_SIZE = 200;
+export const LEGACY_BATCH_THROUGHPUT_TEST_NUMBER_OF_REQUESTS = 1000;
+export const LEGACY_BATCH_TOPIC_NAME = 'legacy_batch_topic';
+export const KAFKA_BOOTSTRAP_SERVERS = ['localhost:9092'];
+export const MODULE_SET_TAGS = ['tagA','tagB','tagC',' tagD']
-export function recordTimeInSeconds(functionToExecute) {
- const startTimeInMillis = Date.now();
- functionToExecute();
- const endTimeInMillis = Date.now();
- const totalTimeInSeconds = (endTimeInMillis - startTimeInMillis) / 1000.0;
- return totalTimeInSeconds;
-}
/**
* Generates a batch of CM-handle IDs based on batch size and number.
@@ -39,33 +40,76 @@ export function recordTimeInSeconds(functionToExecute) {
* @returns {string[]} Array of CM-handle IDs, for example ['ch-201', 'ch-202' ... 'ch-300']
*/
export function makeBatchOfCmHandleIds(batchSize, batchNumber) {
- const batchOfIds = [];
const startIndex = 1 + batchNumber * batchSize;
- for (let i = 0; i < batchSize; i++) {
- let cmHandleId = `ch-${startIndex + i}`;
- batchOfIds.push(cmHandleId);
- }
- return batchOfIds;
+ return Array.from({ length: batchSize }, (_, i) => `ch-${startIndex + i}`);
+}
+
+/**
+ * Helper function to perform POST requests with JSON payload and content type.
+ * @param {string} url - The URL to send the POST request to.
+ * @param {Object} payload - The JSON payload to send in the POST request.
+ * @param {string} metricTag - A tag for the metric endpoint.
+ * @returns {Object} The response from the HTTP POST request.
+ */
+export function performPostRequest(url, payload, metricTag) {
+ const metricTags = {
+ endpoint: metricTag
+ };
+
+ return http.post(url, payload, {
+ headers: CONTENT_TYPE_JSON_PARAM,
+ tags: metricTags
+ });
}
-export function getRandomCmHandleId() {
- return `ch-${Math.floor(Math.random() * TOTAL_CM_HANDLES) + 1}`;
+/**
+ * Helper function to perform GET requests with metric tags.
+ *
+ * This function sends an HTTP GET request to the specified URL and attaches
+ * a metric tag to the request, which is useful for monitoring and analytics.
+ *
+ * @param {string} url - The URL to which the GET request will be sent.
+ * @param {string} metricTag - A string representing the metric tag to associate with the request.
+ * This tag is used for monitoring and tracking the request.
+ * @returns {Object} The response from the HTTP GET request. The response includes the status code,
+ * headers, body, and other related information.
+ */
+export function performGetRequest(url, metricTag) {
+ const metricTags = {
+ endpoint: metricTag
+ };
+ return http.get(url, {tags: metricTags});
}
-export function makeCustomSummaryReport(data, options) {
- let summaryCsv = '#,Test Name,Unit,Limit,Actual\n';
- summaryCsv += makeSummaryCsvLine(1, 'Registration of CM-handles', 'CM-handles/second', 'cmhandles_created_per_second', data, options);
- summaryCsv += makeSummaryCsvLine(2, 'De-registration of CM-handles', 'CM-handles/second', 'cmhandles_deleted_per_second', data, options);
- summaryCsv += makeSummaryCsvLine(3, 'CM-handle ID search with Module filter', 'milliseconds', 'http_req_duration{scenario:id_search_module}', data, options);
- summaryCsv += makeSummaryCsvLine(4, 'CM-handle search with Module filter', 'milliseconds', 'http_req_duration{scenario:cm_search_module}', data, options);
- summaryCsv += makeSummaryCsvLine(5, 'Synchronous single CM-handle pass-through read', 'milliseconds', 'http_req_duration{scenario:passthrough_read}', data, options);
- return summaryCsv;
+export function makeCustomSummaryReport(testResults, scenarioConfig) {
+ const summaryCsvLines = [
+ '#,Test Name,Unit,Fs Requirement,Current Expectation,Actual',
+ makeSummaryCsvLine('0', 'HTTP request failures for all tests', 'rate of failed requests', 'http_req_failed', 0, testResults, scenarioConfig),
+ makeSummaryCsvLine('1', 'Registration of CM-handles', 'CM-handles/second', 'cmhandles_created_per_second', 70, testResults, scenarioConfig),
+ makeSummaryCsvLine('2', 'De-registration of CM-handles', 'CM-handles/second', 'cmhandles_deleted_per_second', 90, testResults, scenarioConfig),
+ makeSummaryCsvLine('3a', 'CM-handle ID search with No filter', 'milliseconds', 'id_search_nofilter_duration', 400, testResults, scenarioConfig),
+ makeSummaryCsvLine('3b', 'CM-handle ID search with Module filter', 'milliseconds', 'id_search_module_duration', 200, testResults, scenarioConfig),
+ makeSummaryCsvLine('3c', 'CM-handle ID search with Property filter', 'milliseconds', 'id_search_property_duration', 1300, testResults, scenarioConfig),
+ makeSummaryCsvLine('3d', 'CM-handle ID search with Cps Path filter', 'milliseconds', 'id_search_cpspath_duration', 1300, testResults, scenarioConfig),
+ makeSummaryCsvLine('3e', 'CM-handle ID search with Trust Level filter', 'milliseconds', 'id_search_trustlevel_duration', 10000, testResults, scenarioConfig),
+ makeSummaryCsvLine('4a', 'CM-handle search with No filter', 'milliseconds', 'cm_search_nofilter_duration', 14000, testResults, scenarioConfig),
+ makeSummaryCsvLine('4b', 'CM-handle search with Module filter', 'milliseconds', 'cm_search_module_duration', 16000, testResults, scenarioConfig),
+ makeSummaryCsvLine('4c', 'CM-handle search with Property filter', 'milliseconds', 'cm_search_property_duration', 16000, testResults, scenarioConfig),
+ makeSummaryCsvLine('4d', 'CM-handle search with Cps Path filter', 'milliseconds', 'cm_search_cpspath_duration', 16000, testResults, scenarioConfig),
+ makeSummaryCsvLine('4e', 'CM-handle search with Trust Level filter', 'milliseconds', 'cm_search_trustlevel_duration', 26000, testResults, scenarioConfig),
+ makeSummaryCsvLine('5a', 'NCMP overhead for Synchronous single CM-handle pass-through read', 'milliseconds', 'ncmp_overhead_passthrough_read', 30, testResults, scenarioConfig),
+ makeSummaryCsvLine('5b', 'NCMP overhead for Synchronous single CM-handle pass-through read with alternate id', 'milliseconds', 'ncmp_overhead_passthrough_read_alt_id', 60, testResults, scenarioConfig),
+ makeSummaryCsvLine('6a', 'NCMP overhead for Synchronous single CM-handle pass-through write', 'milliseconds', 'ncmp_overhead_passthrough_write', 30, testResults, scenarioConfig),
+ makeSummaryCsvLine('6b', 'NCMP overhead for Synchronous single CM-handle pass-through write with alternate id', 'milliseconds', 'ncmp_overhead_passthrough_write_alt_id', 60, testResults, scenarioConfig),
+ makeSummaryCsvLine('7', 'Legacy batch read operation', 'events/second', 'legacy_batch_read_cmhandles_per_second', 1500, testResults, scenarioConfig),
+ ];
+ return summaryCsvLines.join('\n') + '\n';
}
-function makeSummaryCsvLine(testCase, testName, unit, thresholdInK6, data, options) {
- const thresholdArray = JSON.parse(JSON.stringify(options.thresholds[thresholdInK6]));
+function makeSummaryCsvLine(testCase, testName, unit, measurementName, currentExpectation, testResults, scenarioConfig) {
+ const thresholdArray = JSON.parse(JSON.stringify(scenarioConfig.thresholds[measurementName]));
const thresholdString = thresholdArray[0];
const [thresholdKey, thresholdOperator, thresholdValue] = thresholdString.split(/\s+/);
- const actualValue = data.metrics[thresholdInK6].values[thresholdKey].toFixed(3);
- return `${testCase},${testName},${unit},${thresholdValue},${actualValue}\n`;
+ const actualValue = testResults.metrics[measurementName].values[thresholdKey].toFixed(3);
+ return `${testCase},${testName},${unit},${thresholdValue},${currentExpectation},${actualValue}`;
}
diff --git a/k6-tests/ncmp/ncmp-kpi.js b/k6-tests/ncmp/ncmp-kpi.js
index 91a38d99ce..8815511bdd 100644
--- a/k6-tests/ncmp/ncmp-kpi.js
+++ b/k6-tests/ncmp/ncmp-kpi.js
@@ -19,77 +19,346 @@
*/
import { check } from 'k6';
-import { Gauge } from 'k6/metrics';
-import { TOTAL_CM_HANDLES, makeCustomSummaryReport, recordTimeInSeconds } from './common/utils.js';
-import { registerAllCmHandles, deregisterAllCmHandles } from './common/cmhandle-crud.js';
+import { Trend } from 'k6/metrics';
+import { Reader } from 'k6/x/kafka';
+import {
+ TOTAL_CM_HANDLES, READ_DATA_FOR_CM_HANDLE_DELAY_MS, WRITE_DATA_FOR_CM_HANDLE_DELAY_MS,
+ makeCustomSummaryReport, makeBatchOfCmHandleIds, LEGACY_BATCH_THROUGHPUT_TEST_BATCH_SIZE,
+ LEGACY_BATCH_TOPIC_NAME, KAFKA_BOOTSTRAP_SERVERS, REGISTRATION_BATCH_SIZE,
+ LEGACY_BATCH_THROUGHPUT_TEST_NUMBER_OF_REQUESTS
+} from './common/utils.js';
+import { createCmHandles, deleteCmHandles, waitForAllCmHandlesToBeReady } from './common/cmhandle-crud.js';
import { executeCmHandleSearch, executeCmHandleIdSearch } from './common/search-base.js';
-import { passthroughRead } from './common/passthrough-read.js';
+import { passthroughRead, passthroughWrite, legacyBatchRead } from './common/passthrough-crud.js';
-let cmHandlesCreatedPerSecondGauge = new Gauge('cmhandles_created_per_second');
-let cmHandlesDeletedPerSecondGauge = new Gauge('cmhandles_deleted_per_second');
+let cmHandlesCreatedPerSecondTrend = new Trend('cmhandles_created_per_second', false);
+let cmHandlesDeletedPerSecondTrend = new Trend('cmhandles_deleted_per_second', false);
+let passthroughReadNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_read', true);
+let passthroughReadNcmpOverheadTrendWithAlternateId = new Trend('ncmp_overhead_passthrough_read_alt_id', true);
+let passthroughWriteNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_write', true);
+let passthroughWriteNcmpOverheadTrendWithAlternateId = new Trend('ncmp_overhead_passthrough_write_alt_id', true);
+let idSearchNoFilterDurationTrend = new Trend('id_search_nofilter_duration', true);
+let idSearchModuleDurationTrend = new Trend('id_search_module_duration', true);
+let idSearchPropertyDurationTrend = new Trend('id_search_property_duration', true);
+let idSearchCpsPathDurationTrend = new Trend('id_search_cpspath_duration', true);
+let idSearchTrustLevelDurationTrend = new Trend('id_search_trustlevel_duration', true);
+let cmSearchNoFilterDurationTrend = new Trend('cm_search_nofilter_duration', true);
+let cmSearchModuleDurationTrend = new Trend('cm_search_module_duration', true);
+let cmSearchPropertyDurationTrend = new Trend('cm_search_property_duration', true);
+let cmSearchCpsPathDurationTrend = new Trend('cm_search_cpspath_duration', true);
+let cmSearchTrustLevelDurationTrend = new Trend('cm_search_trustlevel_duration', true);
+let legacyBatchReadCmHandlesPerSecondTrend = new Trend('legacy_batch_read_cmhandles_per_second', false);
+
+const legacyBatchEventReader = new Reader({
+ brokers: KAFKA_BOOTSTRAP_SERVERS,
+ topic: LEGACY_BATCH_TOPIC_NAME,
+});
const DURATION = '15m';
+const LEGACY_BATCH_THROUGHPUT_TEST_START_TIME = '15m30s';
export const options = {
- setupTimeout: '6m',
+ setupTimeout: '8m',
teardownTimeout: '6m',
scenarios: {
- passthrough_read: {
+ passthrough_read_scenario: {
+ executor: 'constant-vus',
+ exec: 'passthroughReadScenario',
+ vus: 2,
+ duration: DURATION,
+ },
+ passthrough_read_alt_id_scenario: {
+ executor: 'constant-vus',
+ exec: 'passthroughReadAltIdScenario',
+ vus: 2,
+ duration: DURATION,
+ },
+ passthrough_write_scenario: {
+ executor: 'constant-vus',
+ exec: 'passthroughWriteScenario',
+ vus: 2,
+ duration: DURATION,
+ },
+ passthrough_write_alt_id_scenario: {
+ executor: 'constant-vus',
+ exec: 'passthroughWriteAltIdScenario',
+ vus: 2,
+ duration: DURATION,
+ },
+ cm_handle_id_search_nofilter_scenario: {
+ executor: 'constant-vus',
+ exec: 'cmHandleIdSearchNoFilterScenario',
+ vus: 1,
+ duration: DURATION,
+ },
+ cm_handle_search_nofilter_scenario: {
+ executor: 'constant-vus',
+ exec: 'cmHandleSearchNoFilterScenario',
+ vus: 1,
+ duration: DURATION,
+ },
+ cm_handle_id_search_module_scenario: {
+ executor: 'constant-vus',
+ exec: 'cmHandleIdSearchModuleScenario',
+ vus: 1,
+ duration: DURATION,
+ },
+ cm_handle_search_module_scenario: {
+ executor: 'constant-vus',
+ exec: 'cmHandleSearchModuleScenario',
+ vus: 1,
+ duration: DURATION,
+ },
+ cm_handle_id_search_property_scenario: {
+ executor: 'constant-vus',
+ exec: 'cmHandleIdSearchPropertyScenario',
+ vus: 1,
+ duration: DURATION,
+ },
+ cm_handle_search_property_scenario: {
executor: 'constant-vus',
- exec: 'passthrough_read',
- vus: 10,
+ exec: 'cmHandleSearchPropertyScenario',
+ vus: 1,
duration: DURATION,
},
- id_search_module: {
+ cm_handle_id_search_cpspath_scenario: {
executor: 'constant-vus',
- exec: 'id_search_module',
- vus: 3,
+ exec: 'cmHandleIdSearchCpsPathScenario',
+ vus: 1,
duration: DURATION,
},
- cm_search_module: {
+ cm_handle_search_cpspath_scenario: {
executor: 'constant-vus',
- exec: 'cm_search_module',
- vus: 3,
+ exec: 'cmHandleSearchCpsPathScenario',
+ vus: 1,
duration: DURATION,
},
+ cm_handle_id_search_trustlevel_scenario: {
+ executor: 'constant-vus',
+ exec: 'cmHandleIdSearchTrustLevelScenario',
+ vus: 1,
+ duration: DURATION,
+ },
+ cm_handle_search_trustlevel_scenario: {
+ executor: 'constant-vus',
+ exec: 'cmHandleSearchTrustLevelScenario',
+ vus: 1,
+ duration: DURATION,
+ },
+ legacy_batch_produce_scenario: {
+ executor: 'shared-iterations',
+ exec: 'legacyBatchProduceScenario',
+ vus: 2,
+ iterations: LEGACY_BATCH_THROUGHPUT_TEST_NUMBER_OF_REQUESTS,
+ startTime: LEGACY_BATCH_THROUGHPUT_TEST_START_TIME,
+ },
+ legacy_batch_consume_scenario: {
+ executor: 'per-vu-iterations',
+ exec: 'legacyBatchConsumeScenario',
+ vus: 1,
+ iterations: 1,
+ startTime: LEGACY_BATCH_THROUGHPUT_TEST_START_TIME,
+ }
},
thresholds: {
- 'cmhandles_created_per_second': ['value >= 22'],
- 'cmhandles_deleted_per_second': ['value >= 22'],
- 'http_req_failed{scenario:passthrough_read}': ['rate == 0'],
- 'http_req_failed{scenario:id_search_module}': ['rate == 0'],
- 'http_req_failed{scenario:cm_search_module}': ['rate == 0'],
- 'http_req_duration{scenario:passthrough_read}': ['avg <= 2600'], // DMI delay + 100 ms
- 'http_req_duration{scenario:id_search_module}': ['avg <= 625'],
- 'http_req_duration{scenario:cm_search_module}': ['avg <= 13000'],
+ 'http_req_failed': ['rate == 0'],
+ 'cmhandles_created_per_second': ['avg >= 22'],
+ 'cmhandles_deleted_per_second': ['avg >= 22'],
+ 'ncmp_overhead_passthrough_read': ['avg <= 40'],
+ 'ncmp_overhead_passthrough_write': ['avg <= 40'],
+ 'ncmp_overhead_passthrough_read_alt_id': ['avg <= 40'],
+ 'ncmp_overhead_passthrough_write_alt_id': ['avg <= 40'],
+ 'id_search_nofilter_duration': ['avg <= 2000'],
+ 'id_search_module_duration': ['avg <= 2000'],
+ 'id_search_property_duration': ['avg <= 2000'],
+ 'id_search_cpspath_duration': ['avg <= 2000'],
+ 'id_search_trustlevel_duration': ['avg <= 2000'],
+ 'cm_search_nofilter_duration': ['avg <= 15000'],
+ 'cm_search_module_duration': ['avg <= 15000'],
+ 'cm_search_property_duration': ['avg <= 15000'],
+ 'cm_search_cpspath_duration': ['avg <= 15000'],
+ 'cm_search_trustlevel_duration': ['avg <= 15000'],
+ 'legacy_batch_read_cmhandles_per_second': ['avg >= 150'],
},
};
export function setup() {
- const totalRegistrationTimeInSeconds = recordTimeInSeconds(registerAllCmHandles);
- cmHandlesCreatedPerSecondGauge.add(TOTAL_CM_HANDLES / totalRegistrationTimeInSeconds);
+ const startTimeInMillis = Date.now();
+
+ const TOTAL_BATCHES = Math.ceil(TOTAL_CM_HANDLES / REGISTRATION_BATCH_SIZE);
+ for (let batchNumber = 0; batchNumber < TOTAL_BATCHES; batchNumber++) {
+ const nextBatchOfCmHandleIds = makeBatchOfCmHandleIds(REGISTRATION_BATCH_SIZE, batchNumber);
+ const response = createCmHandles(nextBatchOfCmHandleIds);
+ check(response, { 'create CM-handles status equals 200': (r) => r.status === 200 });
+ }
+
+ waitForAllCmHandlesToBeReady();
+
+ const endTimeInMillis = Date.now();
+ const totalRegistrationTimeInSeconds = (endTimeInMillis - startTimeInMillis) / 1000.0;
+
+ cmHandlesCreatedPerSecondTrend.add(TOTAL_CM_HANDLES / totalRegistrationTimeInSeconds);
}
export function teardown() {
- const totalDeregistrationTimeInSeconds = recordTimeInSeconds(deregisterAllCmHandles);
- cmHandlesDeletedPerSecondGauge.add(TOTAL_CM_HANDLES / totalDeregistrationTimeInSeconds);
+ const startTimeInMillis = Date.now();
+
+ let DEREGISTERED_CM_HANDLES = 0
+ const TOTAL_BATCHES = Math.ceil(TOTAL_CM_HANDLES / REGISTRATION_BATCH_SIZE);
+ for (let batchNumber = 0; batchNumber < TOTAL_BATCHES; batchNumber++) {
+ const nextBatchOfCmHandleIds = makeBatchOfCmHandleIds(REGISTRATION_BATCH_SIZE, batchNumber);
+ const response = deleteCmHandles(nextBatchOfCmHandleIds);
+ if (response.error_code === 0) {
+ DEREGISTERED_CM_HANDLES += REGISTRATION_BATCH_SIZE
+ }
+ check(response, { 'delete CM-handles status equals 200': (r) => r.status === 200 });
+ }
+
+ const endTimeInMillis = Date.now();
+ const totalDeregistrationTimeInSeconds = (endTimeInMillis - startTimeInMillis) / 1000.0;
+
+ cmHandlesDeletedPerSecondTrend.add(DEREGISTERED_CM_HANDLES / totalDeregistrationTimeInSeconds);
+}
+
+export function passthroughReadScenario() {
+ const response = passthroughRead(false);
+ if (check(response, { 'passthrough read status equals 200': (r) => r.status === 200 })) {
+ const overhead = response.timings.duration - READ_DATA_FOR_CM_HANDLE_DELAY_MS;
+ passthroughReadNcmpOverheadTrend.add(overhead);
+ }
+}
+
+export function passthroughReadAltIdScenario() {
+ const response = passthroughRead(true);
+ if (check(response, { 'passthrough read with alternate Id status equals 200': (r) => r.status === 200 })) {
+ const overhead = response.timings.duration - READ_DATA_FOR_CM_HANDLE_DELAY_MS;
+ passthroughReadNcmpOverheadTrendWithAlternateId.add(overhead);
+ }
+}
+
+export function passthroughWriteScenario() {
+ const response = passthroughWrite(false);
+ if (check(response, { 'passthrough write status equals 201': (r) => r.status === 201 })) {
+ const overhead = response.timings.duration - WRITE_DATA_FOR_CM_HANDLE_DELAY_MS;
+ passthroughWriteNcmpOverheadTrend.add(overhead);
+ }
+}
+
+export function passthroughWriteAltIdScenario() {
+ const response = passthroughWrite(true);
+ if (check(response, { 'passthrough write with alternate Id status equals 201': (r) => r.status === 201 })) {
+ const overhead = response.timings.duration - WRITE_DATA_FOR_CM_HANDLE_DELAY_MS;
+ passthroughWriteNcmpOverheadTrendWithAlternateId.add(overhead);
+ }
}
-export function passthrough_read() {
- const response = passthroughRead();
- check(response, { 'passthrough read status equals 200': (r) => r.status === 200 });
+export function cmHandleIdSearchNoFilterScenario() {
+ const response = executeCmHandleIdSearch('no-filter');
+ if (check(response, { 'CM handle ID no-filter search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle ID no-filter search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ idSearchNoFilterDurationTrend.add(response.timings.duration);
+ }
}
-export function id_search_module() {
+export function cmHandleSearchNoFilterScenario() {
+ const response = executeCmHandleSearch('no-filter');
+ if (check(response, { 'CM handle no-filter search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle no-filter search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ cmSearchNoFilterDurationTrend.add(response.timings.duration);
+ }
+}
+
+export function cmHandleIdSearchModuleScenario() {
const response = executeCmHandleIdSearch('module');
- check(response, { 'module ID search status equals 200': (r) => r.status === 200 });
- check(JSON.parse(response.body), { 'module ID search returned expected CM-handles': (arr) => arr.length === TOTAL_CM_HANDLES });
+ if (check(response, { 'CM handle ID module search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle ID module search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ idSearchModuleDurationTrend.add(response.timings.duration);
+ }
}
-export function cm_search_module() {
+export function cmHandleSearchModuleScenario() {
const response = executeCmHandleSearch('module');
- check(response, { 'module search status equals 200': (r) => r.status === 200 });
- check(JSON.parse(response.body), { 'module search returned expected CM-handles': (arr) => arr.length === TOTAL_CM_HANDLES });
+ if (check(response, { 'CM handle module search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle module search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ cmSearchModuleDurationTrend.add(response.timings.duration);
+ }
+}
+
+export function cmHandleIdSearchPropertyScenario() {
+ const response = executeCmHandleIdSearch('property');
+ if (check(response, { 'CM handle ID property search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle ID property search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ idSearchPropertyDurationTrend.add(response.timings.duration);
+ }
+}
+
+export function cmHandleSearchPropertyScenario() {
+ const response = executeCmHandleSearch('property');
+ if (check(response, { 'CM handle property search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle property search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ cmSearchPropertyDurationTrend.add(response.timings.duration);
+ }
+}
+
+export function cmHandleIdSearchCpsPathScenario() {
+ const response = executeCmHandleIdSearch('cps-path-for-ready-cm-handles');
+ if (check(response, { 'CM handle ID cps path search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle ID cps path search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ idSearchCpsPathDurationTrend.add(response.timings.duration);
+ }
+}
+
+export function cmHandleSearchCpsPathScenario() {
+ const response = executeCmHandleSearch('cps-path-for-ready-cm-handles');
+ if (check(response, { 'CM handle cps path search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle cps path search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ cmSearchCpsPathDurationTrend.add(response.timings.duration);
+ }
+}
+
+export function cmHandleIdSearchTrustLevelScenario() {
+ const response = executeCmHandleIdSearch('trust-level');
+ if (check(response, { 'CM handle ID trust level search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle ID trust level search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ idSearchTrustLevelDurationTrend.add(response.timings.duration);
+ }
+}
+
+export function cmHandleSearchTrustLevelScenario() {
+ const response = executeCmHandleSearch('trust-level');
+ if (check(response, { 'CM handle trust level search status equals 200': (r) => r.status === 200 })
+ && check(response, { 'CM handle trust level search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
+ cmSearchTrustLevelDurationTrend.add(response.timings.duration);
+ }
+}
+
+export function legacyBatchProduceScenario() {
+ const nextBatchOfCmHandleIds = makeBatchOfCmHandleIds(LEGACY_BATCH_THROUGHPUT_TEST_BATCH_SIZE, 0);
+ const response = legacyBatchRead(nextBatchOfCmHandleIds);
+ check(response, { 'data operation batch read status equals 200': (r) => r.status === 200 });
+}
+
+export function legacyBatchConsumeScenario() {
+ const TOTAL_MESSAGES_TO_CONSUME = LEGACY_BATCH_THROUGHPUT_TEST_NUMBER_OF_REQUESTS * LEGACY_BATCH_THROUGHPUT_TEST_BATCH_SIZE;
+ try {
+ let messagesConsumed = 0;
+ let startTime = Date.now();
+
+ while (messagesConsumed < TOTAL_MESSAGES_TO_CONSUME) {
+ let messages = legacyBatchEventReader.consume({ limit: 1000 });
+
+ if (messages.length > 0) {
+ messagesConsumed += messages.length;
+ }
+ }
+
+ let endTime = Date.now();
+ const timeToConsumeMessagesInSeconds = (endTime - startTime) / 1000.0;
+ legacyBatchReadCmHandlesPerSecondTrend.add(TOTAL_MESSAGES_TO_CONSUME / timeToConsumeMessagesInSeconds);
+ } catch (error) {
+ legacyBatchReadCmHandlesPerSecondTrend.add(0);
+ console.error(error);
+ }
}
export function handleSummary(data) {
diff --git a/k6-tests/ncmp/run-all-tests.sh b/k6-tests/ncmp/run-all-tests.sh
index 2db32ecd76..1fa661a472 100755
--- a/k6-tests/ncmp/run-all-tests.sh
+++ b/k6-tests/ncmp/run-all-tests.sh
@@ -19,7 +19,9 @@ pushd "$(dirname "$0")" >/dev/null || exit 1
number_of_failures=0
echo "Running K6 performance tests..."
-k6 --quiet run ncmp-kpi.js > summary.csv || ((number_of_failures++))
+
+# Redirecting stderr to /dev/null to prevent large log files
+k6 --quiet run ncmp-kpi.js > summary.csv 2>/dev/null || ((number_of_failures++))
if [ -f summary.csv ]; then
diff --git a/k6-tests/once-off-test/kafka/produce-avc-event.js b/k6-tests/once-off-test/kafka/produce-avc-event.js
new file mode 100644
index 0000000000..981a21af65
--- /dev/null
+++ b/k6-tests/once-off-test/kafka/produce-avc-event.js
@@ -0,0 +1,99 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+import { crypto } from 'k6/experimental/webcrypto';
+import { check } from 'k6';
+import { Writer, SchemaRegistry, SCHEMA_TYPE_STRING } from 'k6/x/kafka';
+
+const testEventPayload = JSON.stringify(JSON.parse(open('../../resources/sampleAvcInputEvent.json')));
+const schemaRegistry = new SchemaRegistry();
+const kafkaProducer = new Writer({
+ brokers: ['localhost:9092'],
+ topic: 'dmi-cm-events',
+ autoCreateTopic: true,
+ batchSize: 5000,
+ compression: 'gzip',
+ requestTimeout: 30000
+});
+
+const TOTAL_MESSAGES = 100000;
+const VIRTUAL_USERS = 1000;
+
+export const options = {
+ setupTimeout: '1m',
+ teardownTimeout: '1m',
+ scenarios: {
+ produceKafkaMessages: {
+ executor: 'shared-iterations',
+ exec: 'sendKafkaMessages',
+ vus: VIRTUAL_USERS,
+ iterations: TOTAL_MESSAGES,
+ maxDuration: '10m',
+ }
+ }
+};
+
+function getCloudEventHeaders() {
+ return {
+ ce_type: 'org.onap.cps.ncmp.events.avc1_0_0.AvcEvent',
+ ce_source: 'DMI',
+ ce_destination: 'dmi-cm-events',
+ ce_specversion: '1.0',
+ ce_time: new Date().toISOString(),
+ ce_id: crypto.randomUUID(),
+ ce_dataschema: 'urn:cps:org.onap.cps.ncmp.events.avc1_0_0.AvcEvent:1.0.0',
+ ce_correlationid: crypto.randomUUID()
+ };
+}
+
+export function sendKafkaMessages() {
+ const cloudEventHeaders = getCloudEventHeaders();
+
+ const avcCloudEvent = {
+ key: schemaRegistry.serialize({
+ data: cloudEventHeaders.ce_correlationid,
+ schemaType: SCHEMA_TYPE_STRING,
+ }),
+ value: schemaRegistry.serialize({
+ data: testEventPayload,
+ schemaType: SCHEMA_TYPE_STRING
+ }),
+ headers: cloudEventHeaders
+ };
+
+ try {
+ kafkaProducer.produce({ messages: [avcCloudEvent] });
+
+ const isMessageSent = check(kafkaProducer, {
+ 'Message sent successfully': (producer) => producer != null,
+ });
+
+ if (!isMessageSent) {
+ console.error('Failed to send message:', avcCloudEvent);
+ }
+
+ } catch (error) {
+ console.error('Error during message production:', error, avcCloudEvent);
+ }
+}
+
+export function teardown() {
+ kafkaProducer.close();
+}
diff --git a/k6-tests/resources/sampleAvcInputEvent.json b/k6-tests/resources/sampleAvcInputEvent.json
new file mode 100644
index 0000000000..4c9cd721df
--- /dev/null
+++ b/k6-tests/resources/sampleAvcInputEvent.json
@@ -0,0 +1,38 @@
+{
+ "data": {
+ "push-change-update": {
+ "datastore-changes": {
+ "ietf-yang-patch:yang-patch": {
+ "patch-id": "34534ffd98",
+ "edit": [
+ {
+ "edit-id": "ded43434-1",
+ "operation": "replace",
+ "target": "ancestor:ancestor/parent[@id='parent1']/child[@id='child1']/grandchild[@id='grandchild1']/relation[@id='relation1']",
+ "value": {
+ "attributes": []
+ }
+ },
+ {
+ "edit-id": "ded43434-2",
+ "operation": "create",
+ "target": "ancestor:ancestor/parent[@id='parent1']/child[@id='child1']/grandchild[@id='grandchild1']/relation[@id='relation1']",
+ "value": {
+ "attributes": [
+ {
+ "isHoAllowed": false
+ }
+ ]
+ }
+ },
+ {
+ "edit-id": "ded43434-3",
+ "operation": "delete",
+ "target": "ancestor:ancestor/parent[@id='parent1']/child[@id='child1']/grandchild[@id='grandchild1']/relation[@id='relation1']"
+ }
+ ]
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/k6-tests/run-k6-tests.sh b/k6-tests/run-k6-tests.sh
index 9b8747b1ff..b1ad38911a 100755
--- a/k6-tests/run-k6-tests.sh
+++ b/k6-tests/run-k6-tests.sh
@@ -31,6 +31,10 @@ trap on_exit EXIT
pushd "$(dirname "$0")" || exit 1
+# Install needed dependencies.
+source install-deps.sh
+
+# Run k6 test suite.
./setup.sh
./ncmp/run-all-tests.sh
NCMP_RESULT=$?
diff --git a/k6-tests/setup.sh b/k6-tests/setup.sh
index 86f6d26acd..c2cdc206be 100755
--- a/k6-tests/setup.sh
+++ b/k6-tests/setup.sh
@@ -18,7 +18,7 @@
docker-compose -f ../docker-compose/docker-compose.yml --profile dmi-stub up -d
echo "Waiting for CPS to start..."
-READY_MESSAGE="Processing module sync fetched 0 advised cm handles from DB"
+READY_MESSAGE="Inventory Model updated successfully"
# Get the container IDs of the cps-and-ncmp replicas
CONTAINER_IDS=$(docker ps --filter "name=cps-and-ncmp" --format "{{.ID}}")
@@ -26,5 +26,5 @@ CONTAINER_IDS=$(docker ps --filter "name=cps-and-ncmp" --format "{{.ID}}")
# Check the logs for each container
for CONTAINER_ID in $CONTAINER_IDS; do
echo "Checking logs for container: $CONTAINER_ID"
- docker logs "$CONTAINER_ID" -f | grep -m 1 "$READY_MESSAGE" >/dev/null || true
+ docker logs "$CONTAINER_ID" -f | grep -m 1 "$READY_MESSAGE" >/dev/null && echo "CPS is ready in container: $CONTAINER_ID" || true
done
diff --git a/k6-tests/teardown.sh b/k6-tests/teardown.sh
index 45422f9d12..7693dc03a4 100755
--- a/k6-tests/teardown.sh
+++ b/k6-tests/teardown.sh
@@ -18,11 +18,11 @@
echo '================================== docker info =========================='
docker ps -a
-echo 'Stopping, Removing all running containers...'
-docker stop $(docker ps -aq) && docker rm $(docker ps -aq)
-
-echo 'Removing Volumes...'
-docker volume prune -f
-
-echo 'Removing Networks...'
-docker network prune -f
+echo 'Stopping, Removing containers and volumes...'
+docker_compose_cmd="docker-compose -f ../docker-compose/docker-compose.yml --profile dmi-stub down --volumes"
+# Set an environment variable CLEAN_DOCKER_IMAGES=1 to also remove docker images when done (used on jenkins job)
+if [ "${CLEAN_DOCKER_IMAGES:-0}" -eq 1 ]; then
+ $docker_compose_cmd --rmi all
+else
+ $docker_compose_cmd
+fi
diff --git a/policy-executor-stub/pom.xml b/policy-executor-stub/pom.xml
index 99e621d8a9..3618d7816c 100644
--- a/policy-executor-stub/pom.xml
+++ b/policy-executor-stub/pom.xml
@@ -6,7 +6,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
@@ -22,6 +22,11 @@
</properties>
<dependencies>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ <scope>provided</scope>
+ </dependency>
<!-- S P R I N G D E P E N D E N C I E S -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -163,6 +168,20 @@
</execution>
</executions>
</plugin>
+
+ <plugin>
+ <groupId>org.jsonschema2pojo</groupId>
+ <artifactId>jsonschema2pojo-maven-plugin</artifactId>
+ <configuration>
+ <useJakartaValidation>true</useJakartaValidation>
+ <sourceDirectory>${project.parent.basedir}/../docs/schemas/policy-executor</sourceDirectory>
+ <targetPackage>org.onap.cps.policyexecutor.stub.model</targetPackage>
+ <generateBuilders>true</generateBuilders>
+ <serializable>true</serializable>
+ <includeJsr303Annotations>true</includeJsr303Annotations>
+ </configuration>
+ </plugin>
+
</plugins>
</build>
diff --git a/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java
index a5ec6dcac9..88073c0a0f 100644
--- a/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java
+++ b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java
@@ -20,55 +20,93 @@
package org.onap.cps.policyexecutor.stub.controller;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.onap.cps.policyexecutor.stub.api.PolicyExecutorApi;
+import org.onap.cps.policyexecutor.stub.model.NcmpDelete;
import org.onap.cps.policyexecutor.stub.model.PolicyExecutionRequest;
import org.onap.cps.policyexecutor.stub.model.PolicyExecutionResponse;
+import org.onap.cps.policyexecutor.stub.model.Request;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
-@RequestMapping("${rest.api.policy-executor-base-path}")
+@RequiredArgsConstructor
+@Slf4j
public class PolicyExecutorStubController implements PolicyExecutorApi {
- private final Pattern errorCodePattern = Pattern.compile("(\\d{3})");
+ private final Sleeper sleeper;
+ private final ObjectMapper objectMapper;
+ private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("(\\d{3})");
private int decisionCounter = 0;
+ private static int slowResponseTimeInSeconds = 40;
@Override
public ResponseEntity<PolicyExecutionResponse> executePolicyAction(
final String action,
final PolicyExecutionRequest policyExecutionRequest,
final String authorization) {
- if (policyExecutionRequest.getPayload().isEmpty()) {
+ log.info("Stub Policy Executor Invoked (only supports 'delete' operations)");
+ if (policyExecutionRequest.getRequests().isEmpty()) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
+ final Request firstRequest = policyExecutionRequest.getRequests().iterator().next();
+ log.info("1st Request Schema:{}", firstRequest.getSchema());
+ if (firstRequest.getSchema().contains("ncmp-delete-schema:1.0.0")) {
+ return handleNcmpDeleteSchema(firstRequest);
+ }
+ log.warn("This stub only supports 'delete' operations");
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+
+ private ResponseEntity<PolicyExecutionResponse> handleNcmpDeleteSchema(final Request request) {
+ final NcmpDelete ncmpDelete = objectMapper.convertValue(request.getData(), NcmpDelete.class);
- final String firstTargetFdn = policyExecutionRequest.getPayload().iterator().next().getTargetFdn();
+ final String targetIdentifier = ncmpDelete.getTargetIdentifier();
+
+ if (targetIdentifier == null) {
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
- final Matcher matcher = errorCodePattern.matcher(firstTargetFdn);
+ final Matcher matcher = ERROR_CODE_PATTERN.matcher(targetIdentifier);
if (matcher.find()) {
final int errorCode = Integer.parseInt(matcher.group(1));
return new ResponseEntity<>(HttpStatusCode.valueOf(errorCode));
}
+ return createPolicyExecutionResponse(targetIdentifier);
+ }
+
+ private ResponseEntity<PolicyExecutionResponse> createPolicyExecutionResponse(final String targetIdentifier) {
final String decisionId = String.valueOf(++decisionCounter);
final String decision;
final String message;
-
- if (firstTargetFdn.toLowerCase(Locale.getDefault()).contains("cps-is-great")) {
- decision = "permit";
+ if (targetIdentifier.toLowerCase(Locale.getDefault()).contains("slow")) {
+ try {
+ sleeper.haveALittleRest(slowResponseTimeInSeconds);
+ } catch (final InterruptedException e) {
+ log.trace("Sleep interrupted, re-interrupting the thread");
+ Thread.currentThread().interrupt(); // Re-interrupt the thread
+ }
+ }
+ if (targetIdentifier.toLowerCase(Locale.getDefault()).contains("cps-is-great")) {
+ decision = "allow";
message = "All good";
} else {
decision = "deny";
- message = "Only FDNs containing 'cps-is-great' are permitted";
+ message = "Only FDNs containing 'cps-is-great' are allowed";
}
+ log.info("Decision: {} ({})", decision, message);
final PolicyExecutionResponse policyExecutionResponse =
new PolicyExecutionResponse(decisionId, decision, message);
+
return ResponseEntity.ok(policyExecutionResponse);
}
+
}
diff --git a/k6-tests/ncmp/common/passthrough-read.js b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/Sleeper.java
index 89ed15af79..8f904cc5f2 100644
--- a/k6-tests/ncmp/common/passthrough-read.js
+++ b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/Sleeper.java
@@ -18,15 +18,18 @@
* ============LICENSE_END=========================================================
*/
-import http from 'k6/http';
-import { NCMP_BASE_URL, getRandomCmHandleId } from './utils.js';
+package org.onap.cps.policyexecutor.stub.controller;
-export function passthroughRead() {
- const cmHandleId = getRandomCmHandleId();
- const resourceIdentifier = 'my-resource-identifier';
- const includeDescendants = true;
- const datastoreName = 'ncmp-datastore:passthrough-operational';
- const url = `${NCMP_BASE_URL}/ncmp/v1/ch/${cmHandleId}/data/ds/${datastoreName}?resourceIdentifier=${resourceIdentifier}&include-descendants=${includeDescendants}`
- const response = http.get(url);
- return response;
+import java.util.concurrent.TimeUnit;
+import org.springframework.stereotype.Service;
+
+/**
+ * This class is a successfull experiment to extract out sleep functionality so the interrupted exception handling can
+ * be covered with a test (e.g. using spy ion Sleeper) and help to get too 100% code coverage.
+ */
+@Service
+public class Sleeper {
+ public void haveALittleRest(final int timeInSeconds) throws InterruptedException {
+ TimeUnit.SECONDS.sleep(timeInSeconds);
+ }
}
diff --git a/policy-executor-stub/src/main/resources/application.yml b/policy-executor-stub/src/main/resources/application.yml
index f713a157fe..8ed5742a41 100644
--- a/policy-executor-stub/src/main/resources/application.yml
+++ b/policy-executor-stub/src/main/resources/application.yml
@@ -15,6 +15,5 @@
# SPDX-License-Identifier: Apache-2.0
# ============LICENSE_END=========================================================
-rest:
- api:
- policy-executor-base-path: /policy-executor/api
+server:
+ port: 8093
diff --git a/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy b/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy
index 871db81ac8..44460daa7e 100644
--- a/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy
+++ b/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy
@@ -21,9 +21,11 @@
package org.onap.cps.policyexecutor.stub.controller
import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.policyexecutor.stub.model.Payload
+import org.onap.cps.policyexecutor.stub.model.NcmpDelete
import org.onap.cps.policyexecutor.stub.model.PolicyExecutionRequest
import org.onap.cps.policyexecutor.stub.model.PolicyExecutionResponse
+import org.onap.cps.policyexecutor.stub.model.Request
+import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.HttpStatus
@@ -42,13 +44,18 @@ class PolicyExecutorStubControllerSpec extends Specification {
@Autowired
ObjectMapper objectMapper
+ @SpringBean
+ Sleeper sleeper = Spy()
+
def url = '/policy-executor/api/v1/some-action'
- def 'Execute Policy Actions.'() {
- given: 'a policy execution request with target fdn: #targetFdn'
- def payload = new Payload(targetFdn, 'some change request')
- def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [payload])
- def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
+ def setup() {
+ PolicyExecutorStubController.slowResponseTimeInSeconds = 1
+ }
+
+ def 'Execute policy action.'() {
+ given: 'a policy execution request with target: #targetIdentifier'
+ def requestBody = createRequestBody(targetIdentifier)
when: 'request is posted'
def response = mockMvc.perform(post(url)
.header('Authorization','some string')
@@ -61,19 +68,18 @@ class PolicyExecutorStubControllerSpec extends Specification {
def responseBody = response.contentAsString
def policyExecutionResponse = objectMapper.readValue(responseBody, PolicyExecutionResponse.class)
assert policyExecutionResponse.decisionId == expectedDecsisonId
- assert policyExecutionResponse.decision == expectedDecsison
+ assert policyExecutionResponse.decision == expectedDecision
assert policyExecutionResponse.message == expectedMessage
where: 'the following targets are used'
- targetFdn || expectedDecsisonId | expectedDecsison | expectedMessage
- 'some fdn' || '1' | 'deny' | "Only FDNs containing 'cps-is-great' are permitted"
- 'fdn with cps-is-great' || '2' | 'permit' | "All good"
+ targetIdentifier || expectedDecsisonId | expectedDecision | expectedMessage
+ 'some fdn' || '1' | 'deny' | "Only FDNs containing 'cps-is-great' are allowed"
+ 'fdn with cps-is-great' || '2' | 'allow' | 'All good'
+ 'slow' || '3' | 'deny' | "Only FDNs containing 'cps-is-great' are allowed"
}
- def 'Execute Policy Action with a HTTP Error Code.'() {
+ def 'Execute policy action with a HTTP error code.'() {
given: 'a policy execution request with a target fdn with a 3-digit error code'
- def payload = new Payload('fdn with error code 418', 'some change request')
- def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [payload])
- def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
+ def requestBody = createRequestBody('target with error code 418')
when: 'request is posted'
def response = mockMvc.perform(post(url)
.header('Authorization','some string')
@@ -84,11 +90,9 @@ class PolicyExecutorStubControllerSpec extends Specification {
assert response.status == 418
}
- def 'Execute Policy Action without Authorization Header.'() {
+ def 'Execute policy action without authorization header.'() {
given: 'a valid policy execution request'
- def payload = new Payload('some fdn', 'some change request')
- def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [payload])
- def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
+ def requestBody = createRequestBody('some target')
when: 'request is posted without authorization header'
def response = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON)
@@ -98,9 +102,9 @@ class PolicyExecutorStubControllerSpec extends Specification {
assert response.status == HttpStatus.OK.value()
}
- def 'Execute Policy Action with Empty Payload.'() {
- given: 'a policy execution request with empty payload list'
- def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [])
+ def 'Execute policy action with no requests.'() {
+ given: 'a policy execution request'
+ def policyExecutionRequest = new PolicyExecutionRequest('some decision type', [])
def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
when: 'request is posted'
def response = mockMvc.perform(post(url)
@@ -112,11 +116,33 @@ class PolicyExecutorStubControllerSpec extends Specification {
assert response.status == HttpStatus.BAD_REQUEST.value()
}
- def 'Execute Policy Action without other required attributes.'() {
- given: 'a policy execution request with payloadType=#payloadType, decisionType=decisionType, targetFdn=#targetFdn, changeRequest=#changeRequest'
- def payload = new Payload(targetFdn, changeRequest)
- def policyExecutionRequest = new PolicyExecutionRequest(payloadType, decisionType, [payload])
- def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
+ def 'Execute policy action with invalid json for request data.'() {
+ when: 'request is posted'
+ def response = mockMvc.perform(post(url)
+ .header('Authorization','some string')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content('invalid json'))
+ .andReturn().response
+ then: 'response status is Bad Request'
+ assert response.status == HttpStatus.BAD_REQUEST.value()
+ }
+
+ def 'Execute policy action with interrupted exception during slow response.'() {
+ given: 'a policy execution request with target: "slow"'
+ def requestBody = createRequestBody('slow')
+ sleeper.haveALittleRest(_) >> { throw new InterruptedException() }
+ when: 'request is posted'
+ mockMvc.perform(post(url)
+ .header('Authorization','some string')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(requestBody))
+ then: 'response status is Bad Request'
+ noExceptionThrown()
+ }
+
+ def 'Execute policy action with missing or invalid attributes.'() {
+ given: 'a policy execution request with decisionType=#decisionType, schema=#schema, targetIdentifier=#targetIdentifier'
+ def requestBody = createRequestBody(decisionType, schema, targetIdentifier)
when: 'request is posted'
def response = mockMvc.perform(post(url)
.header('Authorization','something')
@@ -125,13 +151,23 @@ class PolicyExecutorStubControllerSpec extends Specification {
.andReturn().response
then: 'response status as expected'
assert response.status == expectedStatus.value()
- where: 'following parameters are populated or not'
- payloadType | decisionType | targetFdn | changeRequest || expectedStatus
- 'something' | 'something' | 'something' | 'something' || HttpStatus.OK
- null | 'something' | 'something' | 'something' || HttpStatus.BAD_REQUEST
- 'something' | null | 'something' | 'something' || HttpStatus.BAD_REQUEST
- 'something' | 'something' | null | 'something' || HttpStatus.BAD_REQUEST
- 'something' | 'something' | 'something' | null || HttpStatus.BAD_REQUEST
+ where: 'following parameters are used'
+ decisionType | schema | targetIdentifier || expectedStatus
+ 'something' | 'ncmp-delete-schema:1.0.0' | 'something' || HttpStatus.OK
+ null | 'ncmp-delete-schema:1.0.0' | 'something' || HttpStatus.BAD_REQUEST
+ 'something' | 'other schema' | 'something' || HttpStatus.BAD_REQUEST
+ 'something' | 'ncmp-delete-schema:1.0.0' | null || HttpStatus.BAD_REQUEST
+ }
+
+ def createRequestBody(decisionType, schema, targetIdentifier) {
+ def ncmpDelete = new NcmpDelete(targetIdentifier: targetIdentifier)
+ def request = new Request(schema, ncmpDelete)
+ def policyExecutionRequest = new PolicyExecutionRequest(decisionType, [request])
+ return objectMapper.writeValueAsString(policyExecutionRequest)
+ }
+
+ def createRequestBody(targetIdentifier) {
+ return createRequestBody('some decision type', 'ncmp-delete-schema:1.0.0', targetIdentifier)
}
}
diff --git a/pom.xml b/pom.xml
index 9a06b7bb5b..6c34a19ed1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
<groupId>org.onap.cps</groupId>
<artifactId>cps-aggregator</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<packaging>pom</packaging>
<name>cps</name>
@@ -61,7 +61,6 @@
<module>cps-ncmp-rest-stub</module>
<module>cps-path-parser</module>
<module>cps-ri</module>
- <module>dmi-plugin-demo-and-csit-stub</module>
<module>integration-test</module>
<module>checkstyle</module>
<module>spotbugs</module>
diff --git a/postman-collections/CPS Environment.postman_environment.json b/postman-collections/CPS Environment.postman_environment.json
index 84dcb642d7..7ee9c6779e 100644
--- a/postman-collections/CPS Environment.postman_environment.json
+++ b/postman-collections/CPS Environment.postman_environment.json
@@ -1,5 +1,5 @@
{
- "id": "e8e90dd2-20be-49be-813f-07db44c6a4c2",
+ "id": "fd705c16-c17e-434d-ad2e-ba040a7ed062",
"name": "CPS Environment",
"values": [
{
@@ -16,30 +16,18 @@
},
{
"key": "DMI_HOST",
- "value": "localhost",
+ "value": "ncmp-dmi-plugin-demo-and-csit-stub",
"type": "default",
"enabled": true
},
{
"key": "DMI_PORT",
- "value": "8784",
- "type": "default",
- "enabled": true
- },
- {
- "key": "SDNC_HOST",
- "value": "localhost",
- "type": "default",
- "enabled": true
- },
- {
- "key": "SDNC_PORT",
- "value": "8282",
+ "value": "8092",
"type": "default",
"enabled": true
}
],
"_postman_variable_scope": "environment",
- "_postman_exported_at": "2024-02-23T13:00:33.537Z",
- "_postman_exported_using": "Postman/10.23.4"
-} \ No newline at end of file
+ "_postman_exported_at": "2024-07-30T15:54:00.831Z",
+ "_postman_exported_using": "Postman/11.5.1"
+}
diff --git a/postman-collections/DMI Stub.postman_collection.json b/postman-collections/DMI Stub.postman_collection.json
deleted file mode 100644
index fe7250f7db..0000000000
--- a/postman-collections/DMI Stub.postman_collection.json
+++ /dev/null
@@ -1,161 +0,0 @@
-{
- "info": {
- "_postman_id": "4baf7902-0f1e-49a9-9c6a-f68f412240af",
- "name": "DMI Stub",
- "description": "A collection of the DMI Stub endpoints.",
- "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
- "_exporter_id": "17907116"
- },
- "item": [
- {
- "name": "Execute a data operation for group of cm handle ids by supplied operation details",
- "request": {
- "method": "POST",
- "header": [],
- "body": {
- "mode": "raw",
- "raw": "{ \"operations\":\n [\n {\n \"resourceIdentifier\": \"some resource identifier\",\n \"datastore\": \"ncmp-datastore:passthrough-operational\",\n \"options\": \"some option\",\n \"operationId\": \"12\",\n \"cmHandles\": [\n {\n \"id\": \"cmHandle123\",\n \"cmHandleProperties\": {\n \"myProp\": \"some value\",\n \"otherProp\": \"other value\"\n }\n },\n {\n \"id\": \"cmHandle123\",\n \"cmHandleProperties\": {\n \"myProp\": \"some value\",\n \"otherProp\": \"other value\"\n }\n }\n ],\n \"operation\": \"read\"\n }\n ]\n}",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
- "url": {
- "raw": "http://{{DMI_HOST}}:{{DMI_PORT}}/dmi/v1/data?topic=ncmp-async-m2m&requestId=4753fc1f-7de2-449a-b306-a6204b5370b33",
- "protocol": "http",
- "host": [
- "{{DMI_HOST}}"
- ],
- "port": "{{DMI_PORT}}",
- "path": [
- "dmi",
- "v1",
- "data"
- ],
- "query": [
- {
- "key": "topic",
- "value": "ncmp-async-m2m"
- },
- {
- "key": "requestId",
- "value": "4753fc1f-7de2-449a-b306-a6204b5370b33"
- }
- ]
- }
- },
- "response": []
- },
- {
- "name": "Retrieve module resources for one or more modules",
- "request": {
- "method": "POST",
- "header": [],
- "body": {
- "mode": "raw",
- "raw": "{}",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
- "url": {
- "raw": "http://{{DMI_HOST}}:{{DMI_PORT}}/dmi/v1/ch/cm-bookStore/moduleResources",
- "protocol": "http",
- "host": [
- "{{DMI_HOST}}"
- ],
- "port": "{{DMI_PORT}}",
- "path": [
- "dmi",
- "v1",
- "ch",
- "cm-bookStore",
- "moduleResources"
- ]
- }
- },
- "response": []
- },
- {
- "name": "Get all modules for given cm handle",
- "protocolProfileBehavior": {
- "disabledSystemHeaders": {
- "accept": true
- }
- },
- "request": {
- "method": "POST",
- "header": [
- {
- "key": "Accept",
- "value": "application/json",
- "type": "text"
- }
- ],
- "body": {
- "mode": "raw",
- "raw": "{}",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
- "url": {
- "raw": "http://{{DMI_HOST}}:{{DMI_PORT}}/dmi/v1/ch/cm-bookStore/modules",
- "protocol": "http",
- "host": [
- "{{DMI_HOST}}"
- ],
- "port": "{{DMI_PORT}}",
- "path": [
- "dmi",
- "v1",
- "ch",
- "cm-bookStore",
- "modules"
- ]
- }
- },
- "response": []
- }
- ],
- "auth": {
- "type": "basic",
- "basic": [
- {
- "key": "password",
- "value": "cpsr0cks!",
- "type": "string"
- },
- {
- "key": "username",
- "value": "cpsuser",
- "type": "string"
- }
- ]
- },
- "event": [
- {
- "listen": "prerequest",
- "script": {
- "type": "text/javascript",
- "exec": [
- ""
- ]
- }
- },
- {
- "listen": "test",
- "script": {
- "type": "text/javascript",
- "exec": [
- ""
- ]
- }
- }
- ]
-} \ No newline at end of file
diff --git a/releases/3.5.1-container.yaml b/releases/3.5.1-container.yaml
new file mode 100644
index 0000000000..ddd96ba36d
--- /dev/null
+++ b/releases/3.5.1-container.yaml
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.5.1
+project: cps
+log_dir: cps-maven-docker-stage-master/943/
+ref: 7afd73d95b2de559e728b08abb7f003e70664c44
+containers:
+ - name: 'cps-and-ncmp'
+ version: '3.5.1-20240715T100945Z' \ No newline at end of file
diff --git a/releases/3.5.1.yaml b/releases/3.5.1.yaml
new file mode 100644
index 0000000000..f265e331d0
--- /dev/null
+++ b/releases/3.5.1.yaml
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/951/
+project: cps
+version: 3.5.1 \ No newline at end of file
diff --git a/releases/3.5.2-container.yaml b/releases/3.5.2-container.yaml
new file mode 100644
index 0000000000..a1344fa19c
--- /dev/null
+++ b/releases/3.5.2-container.yaml
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.5.2
+project: cps
+log_dir: cps-maven-docker-stage-master/945/
+ref: 9002b9f6d0d64106e1cbc03d603dc0429da8badd
+containers:
+ - name: 'cps-and-ncmp'
+ version: '3.5.2-20240821T145201Z'
diff --git a/releases/3.5.2.yaml b/releases/3.5.2.yaml
new file mode 100644
index 0000000000..8467524c3b
--- /dev/null
+++ b/releases/3.5.2.yaml
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/953/
+project: cps
+version: 3.5.2
diff --git a/releases/3.5.3-container.yaml b/releases/3.5.3-container.yaml
new file mode 100644
index 0000000000..de99979bb6
--- /dev/null
+++ b/releases/3.5.3-container.yaml
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.5.3
+project: cps
+log_dir: cps-maven-docker-stage-master/946/
+ref: ef70022bddcb18a17c4afe2332cafa78621ab8b5
+containers:
+ - name: 'cps-and-ncmp'
+ version: '3.5.3-20241004T144917Z'
diff --git a/releases/3.5.3.yaml b/releases/3.5.3.yaml
new file mode 100644
index 0000000000..db6944c3c8
--- /dev/null
+++ b/releases/3.5.3.yaml
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/954/
+project: cps
+version: 3.5.3
diff --git a/releases/3.5.4-container.yaml b/releases/3.5.4-container.yaml
new file mode 100644
index 0000000000..7da8f7b56c
--- /dev/null
+++ b/releases/3.5.4-container.yaml
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.5.4
+project: cps
+log_dir: cps-maven-docker-stage-master/947/
+ref: 557404338f8867f8253f82526b43313e669550e0
+containers:
+ - name: 'cps-and-ncmp'
+ version: '3.5.4-20241017T151242Z'
diff --git a/releases/3.5.4.yaml b/releases/3.5.4.yaml
new file mode 100644
index 0000000000..fc987985b5
--- /dev/null
+++ b/releases/3.5.4.yaml
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/955/
+project: cps
+version: 3.5.4
diff --git a/spotbugs/pom.xml b/spotbugs/pom.xml
index 7edfaac7c7..293612e46b 100644
--- a/spotbugs/pom.xml
+++ b/spotbugs/pom.xml
@@ -25,7 +25,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>spotbugs</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<properties>
<nexusproxy>https://nexus.onap.org</nexusproxy>
diff --git a/spotbugs/src/main/resources/spotbugs-exclude.xml b/spotbugs/src/main/resources/spotbugs-exclude.xml
index 78f61d290e..23e62cdbc7 100644
--- a/spotbugs/src/main/resources/spotbugs-exclude.xml
+++ b/spotbugs/src/main/resources/spotbugs-exclude.xml
@@ -36,6 +36,7 @@
<Bug pattern="NP_NULL_PARAM_DEREF" />
<Bug pattern="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" />
<Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" />
+ <Bug pattern="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE" />
<!-- https://stackoverflow.com/a/34674776. Doesn't detect Lombok All Args Constructor variables being used with map get key and value, which can lead to spotbugs being detected
on used fields -->
diff --git a/version.properties b/version.properties
index 6345596e1e..10de88bf6b 100644
--- a/version.properties
+++ b/version.properties
@@ -22,7 +22,7 @@
major=3
minor=5
-patch=1
+patch=5
base_version=${major}.${minor}.${patch}