summaryrefslogtreecommitdiffstats
path: root/dmi-service/src/main/java/org
diff options
context:
space:
mode:
Diffstat (limited to 'dmi-service/src/main/java/org')
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/Application.java33
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/DmiConfiguration.java86
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/DmiPluginConfig.java51
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/WebSecurityConfig.java105
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/kafka/KafkaConfig.java142
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/datajobs/rest/controller/DmiDatajobsRestController.java60
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/CloudEventConstructionException.java37
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/CmHandleRegistrationException.java36
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiException.java59
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiExceptionHandler.java74
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/HttpClientRequestException.java44
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/InvalidDatastoreException.java32
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/ModuleResourceNotFoundException.java38
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/ModulesNotFoundException.java38
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/SdncException.java49
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/AsyncTaskExecutor.java121
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/DmiAsyncRequestResponseEventCreator.java90
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/DmiAsyncRequestResponseEventProducer.java47
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcCloudEventCreator.java104
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcEventProducer.java50
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcEventSimulationController.java61
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumer.java108
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiOutEventToCloudEventMapper.java62
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/model/CmNotificationSubscriptionStatus.java32
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/mapper/CloudEventMapper.java62
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java253
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/handlers/DatastoreType.java65
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java95
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java196
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/YangResourceExtractor.java59
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/client/NcmpRestClient.java64
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClient.java87
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/CmHandleOperation.java35
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/CreatedCmHandle.java36
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleReference.java37
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchema.java36
-rw-r--r--dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java270
37 files changed, 2854 insertions, 0 deletions
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/Application.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/Application.java
new file mode 100644
index 00000000..69d21ba1
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/Application.java
@@ -0,0 +1,33 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(final String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/DmiConfiguration.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/DmiConfiguration.java
new file mode 100644
index 00000000..83ef6f89
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/DmiConfiguration.java
@@ -0,0 +1,86 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.config;
+
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Provides access to cps base url and cps authentication.
+ */
+@Configuration
+public class DmiConfiguration {
+
+ private static final int TIMEOUT = 5000;
+
+ @Getter
+ @Component
+ public static class CpsProperties {
+
+ @Value("${cps-core.baseUrl}")
+ private String baseUrl;
+ @Value("${cps-core.dmiRegistrationUrl}")
+ private String dmiRegistrationUrl;
+ @Value("${cps-core.auth.username}")
+ private String authUsername;
+ @Value("${cps-core.auth.password}")
+ private String authPassword;
+ }
+
+ @Getter
+ @Component
+ public static class SdncProperties {
+
+ @Value("${sdnc.baseUrl}")
+ private String baseUrl;
+ @Value("${sdnc.auth.username}")
+ private String authUsername;
+ @Value("${sdnc.auth.password}")
+ private String authPassword;
+ @Value("${sdnc.topologyId}")
+ public String topologyId;
+ }
+
+ /**
+ * Returns restTemplate bean for the spring context.
+ *
+ * @param restTemplateBuilder restTemplate builder
+ * @return {@code RestTemplate} rest template
+ */
+ @Bean
+ public RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder) {
+ final RestTemplate restTemplate = restTemplateBuilder.build();
+ setCustomRequestFactoryToSupportPatch(restTemplate);
+ return restTemplate;
+ }
+
+ private void setCustomRequestFactoryToSupportPatch(final RestTemplate restTemplate) {
+ final HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
+ requestFactory.setConnectTimeout(TIMEOUT);
+ restTemplate.setRequestFactory(requestFactory);
+ }
+} \ No newline at end of file
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/DmiPluginConfig.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/DmiPluginConfig.java
new file mode 100644
index 00000000..fb22b358
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/DmiPluginConfig.java
@@ -0,0 +1,51 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.config;
+
+import lombok.Getter;
+import org.springdoc.core.models.GroupedOpenApi;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+
+@Configuration
+public class DmiPluginConfig {
+
+ /**
+ * Swagger-ui configuration using springdoc.
+ */
+ @Bean("dmi-plugin-api")
+ public GroupedOpenApi api() {
+ return GroupedOpenApi.builder().group("dmi-plugin-api")
+ .pathsToMatch("/swagger-ui/**,/swagger-resources/**,/v3/api-docs").build();
+ }
+
+ @Getter
+ @Component
+ public static class DmiPluginProperties {
+
+ @Value("${dmi.service.url}")
+ private String dmiServiceUrl;
+ }
+}
+
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/WebSecurityConfig.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/WebSecurityConfig.java
new file mode 100644
index 00000000..ac92cb4a
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/WebSecurityConfig.java
@@ -0,0 +1,105 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.SecurityFilterChain;
+
+/**
+ * Configuration class to implement application security.
+ * It enforces Basic Authentication access control.
+ */
+@Configuration
+@EnableWebSecurity
+public class WebSecurityConfig {
+
+ private static final String USER_ROLE = "USER";
+
+ private final String username;
+ private final String password;
+ private final String[] permitUris;
+
+ /**
+ * Constructor. Accepts parameters from configuration.
+ *
+ * @param permitUris comma-separated list of uri patterns for endpoints permitted
+ * @param username username
+ * @param password password
+ */
+ public WebSecurityConfig(
+ @Autowired @Value("${security.permit-uri}") final String permitUris,
+ @Autowired @Value("${security.auth.username}") final String username,
+ @Autowired @Value("${security.auth.password}") final String password
+ ) {
+ super();
+ this.permitUris = permitUris.isEmpty() ? new String[] {"/v3/api-docs"} : permitUris.split("\\s{0,9},\\s{0,9}");
+ this.username = username;
+ this.password = password;
+ }
+
+ /**
+ * Return the configuration for secure access to the modules REST end points.
+ *
+ * @param http the HTTP security settings.
+ * @return the HTTP security settings.
+ */
+ @Bean
+ // The team decided to disable default CSRF Spring protection and not implement CSRF tokens validation.
+ // ncmp is a stateless REST API that is not as vulnerable to CSRF attacks as web applications running in
+ // web browsers are. ncmp does not manage sessions, each request requires the authentication token in the header.
+ // See https://docs.spring.io/spring-security/site/docs/5.3.8.RELEASE/reference/html5/#csrf
+ @SuppressWarnings("squid:S4502")
+ public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {
+ http
+ .httpBasic(httpBasicCustomizer -> {})
+ .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> {
+ authorizeHttpRequestsCustomizer.requestMatchers(permitUris).permitAll();
+ authorizeHttpRequestsCustomizer.anyRequest().authenticated();
+ })
+ .csrf(AbstractHttpConfigurer::disable);
+
+ return http.build();
+ }
+
+ /**
+ * In memory user authenticaion details.
+ *
+ * @return in memory authentication.
+ */
+ @Bean
+ public InMemoryUserDetailsManager userDetailsService() {
+ final UserDetails user = User.builder()
+ .username(username)
+ .password("{noop}" + password)
+ .roles(USER_ROLE)
+ .build();
+ return new InMemoryUserDetailsManager(user);
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/kafka/KafkaConfig.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/kafka/KafkaConfig.java
new file mode 100644
index 00000000..25ee92ae
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/config/kafka/KafkaConfig.java
@@ -0,0 +1,142 @@
+/*
+ * ============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.config.kafka;
+
+import io.cloudevents.CloudEvent;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.kafka.annotation.EnableKafka;
+import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaProducerFactory;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.kafka.core.ProducerFactory;
+import org.springframework.kafka.support.serializer.JsonDeserializer;
+import org.springframework.kafka.support.serializer.JsonSerializer;
+
+/**
+ * kafka Configuration for legacy and cloud events.
+ *
+ * @param <T> valid legacy event to be published over the wire.
+ */
+@Configuration
+@EnableKafka
+@RequiredArgsConstructor
+public class KafkaConfig<T> {
+
+ private final KafkaProperties kafkaProperties;
+
+ /**
+ * This sets the strategy for creating legacy Kafka producer instance from kafka properties defined into
+ * application.yml and replaces value-serializer by JsonSerializer.
+ *
+ * @return legacy event producer instance.
+ */
+ @Bean
+ public ProducerFactory<String, T> legacyEventProducerFactory() {
+ final Map<String, Object> producerConfigProperties = kafkaProperties.buildProducerProperties();
+ producerConfigProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
+ return new DefaultKafkaProducerFactory<>(producerConfigProperties);
+ }
+
+ /**
+ * The ConsumerFactory implementation is to produce new legacy instance for provided kafka properties defined
+ * into application.yml and replaces deserializer-value by JsonDeserializer.
+ *
+ * @return an instance of legacy consumer factory.
+ */
+ @Bean
+ public ConsumerFactory<String, T> legacyEventConsumerFactory() {
+ final Map<String, Object> consumerConfigProperties = kafkaProperties.buildConsumerProperties();
+ consumerConfigProperties.put("spring.deserializer.value.delegate.class", JsonDeserializer.class);
+ return new DefaultKafkaConsumerFactory<>(consumerConfigProperties);
+ }
+
+ /**
+ * This sets the strategy for creating cloud Kafka producer instance from kafka properties defined into
+ * application.yml with CloudEventSerializer.
+ *
+ * @return cloud event producer instance.
+ */
+ @Bean
+ public ProducerFactory<String, CloudEvent> cloudEventProducerFactory() {
+ final Map<String, Object> producerConfigProperties = kafkaProperties.buildProducerProperties();
+ return new DefaultKafkaProducerFactory<>(producerConfigProperties);
+ }
+
+ /**
+ * The ConsumerFactory implementation to produce new legacy instance for provided kafka properties defined
+ * into application.yml having CloudEventDeserializer as deserializer-value.
+ *
+ * @return an instance of cloud consumer factory.
+ */
+ @Bean
+ public ConsumerFactory<String, CloudEvent> cloudEventConsumerFactory() {
+ final Map<String, Object> consumerConfigProperties = kafkaProperties.buildConsumerProperties();
+ return new DefaultKafkaConsumerFactory<>(consumerConfigProperties);
+ }
+
+ /**
+ * A legacy Kafka event template for executing high-level operations. The legacy producer factory ensure this.
+ *
+ * @return an instance of legacy Kafka template.
+ */
+ @Bean
+ @Primary
+ public KafkaTemplate<String, T> legacyEventKafkaTemplate() {
+ final KafkaTemplate<String, T> kafkaTemplate = new KafkaTemplate<>(legacyEventProducerFactory());
+ kafkaTemplate.setConsumerFactory(legacyEventConsumerFactory());
+ return kafkaTemplate;
+ }
+
+ /**
+ * A cloud Kafka event template for executing high-level operations. The cloud producer factory ensure this.
+ *
+ * @return an instance of cloud Kafka template.
+ */
+ @Bean
+ public KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate() {
+ final KafkaTemplate<String, CloudEvent> kafkaTemplate = new KafkaTemplate<>(cloudEventProducerFactory());
+ kafkaTemplate.setConsumerFactory(cloudEventConsumerFactory());
+ return kafkaTemplate;
+ }
+
+ /**
+ * A cloud Kafka event template for executing high-level operations. The cloud producer factory ensure this.
+ *
+ * @return an instance of cloud Kafka template.
+ */
+ @Bean
+ public ConcurrentKafkaListenerContainerFactory<String, CloudEvent>
+ cloudEventConcurrentKafkaListenerContainerFactory() {
+ final ConcurrentKafkaListenerContainerFactory<String, CloudEvent> containerFactory =
+ new ConcurrentKafkaListenerContainerFactory<>();
+ containerFactory.setConsumerFactory(cloudEventConsumerFactory());
+ return containerFactory;
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/datajobs/rest/controller/DmiDatajobsRestController.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/datajobs/rest/controller/DmiDatajobsRestController.java
new file mode 100644
index 00000000..bbc1c20d
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/datajobs/rest/controller/DmiDatajobsRestController.java
@@ -0,0 +1,60 @@
+/*
+ * ============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.dmi.datajobs.rest.controller;
+
+import org.onap.cps.ncmp.dmi.datajobs.model.SubjobReadRequest;
+import org.onap.cps.ncmp.dmi.datajobs.model.SubjobWriteRequest;
+import org.onap.cps.ncmp.dmi.datajobs.rest.api.DmiDatajobApi;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequestMapping("${rest.api.dmi-base-path}")
+@RestController
+public class DmiDatajobsRestController implements DmiDatajobApi {
+ /**
+ * * This method is not implemented for ONAP DMI plugin.
+ *
+ * @param requestId Identifier for the overall Datajob (required)
+ * @param subjobReadRequest Operation body (optional)
+ * @return (@ code ResponseEntity) response entity
+ */
+ @Override
+ public ResponseEntity<Void> readDataJob(final String requestId,
+ final SubjobReadRequest subjobReadRequest) {
+
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ /**
+ * * This method is not implemented for ONAP DMI plugin.
+ *
+ * @param requestId Identifier for the overall Datajob (required)
+ * @param subjobWriteRequest Operation body (optional)
+ * @return (@ code ResponseEntity) response entity
+ */
+ @Override
+ public ResponseEntity<Void> writeDataJob(final String requestId,
+ final SubjobWriteRequest subjobWriteRequest) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/CloudEventConstructionException.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/CloudEventConstructionException.java
new file mode 100644
index 00000000..f61c156a
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/CloudEventConstructionException.java
@@ -0,0 +1,37 @@
+/*
+ * ============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.dmi.exception;
+
+public class CloudEventConstructionException extends DmiException {
+
+ private static final long serialVersionUID = 7747941311132087621L;
+
+ /**
+ * CloudEventConstructionException.
+ *
+ * @param message the error message
+ * @param details the error details
+ * @param cause the error cause
+ */
+ public CloudEventConstructionException(final String message, final String details, final Throwable cause) {
+ super(message, details, cause);
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/CmHandleRegistrationException.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/CmHandleRegistrationException.java
new file mode 100644
index 00000000..1874389e
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/CmHandleRegistrationException.java
@@ -0,0 +1,36 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.exception;
+
+public class CmHandleRegistrationException extends DmiException {
+
+ private static final long serialVersionUID = 8973438585188332404L;
+
+ /**
+ * Constructor.
+ *
+ * @param details the error details
+ */
+ public CmHandleRegistrationException(final String details) {
+ super("Not able to register the given cm-handles.", details);
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiException.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiException.java
new file mode 100644
index 00000000..c099a1cc
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiException.java
@@ -0,0 +1,59 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.exception;
+
+import lombok.Getter;
+
+/**
+ * Dmi exception.
+ */
+public class DmiException extends RuntimeException {
+
+ private static final long serialVersionUID = 1481520410918497487L;
+
+ @Getter
+ final String details;
+
+ /**
+ * Constructor.
+ *
+ * @param message the error message
+ * @param details the error details
+ */
+ public DmiException(final String message, final String details) {
+ super(message);
+ this.details = details;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message the error message
+ * @param details the error details
+ * @param cause the cause of the exception
+ */
+ public DmiException(final String message, final String details, final Throwable cause) {
+ super(message, cause);
+ this.details = details;
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiExceptionHandler.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiExceptionHandler.java
new file mode 100644
index 00000000..49db7d8b
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/DmiExceptionHandler.java
@@ -0,0 +1,74 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.exception;
+
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.model.ErrorMessage;
+import org.onap.cps.ncmp.dmi.rest.controller.DmiRestController;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@Slf4j
+@RestControllerAdvice(assignableTypes = {DmiRestController.class})
+public class DmiExceptionHandler {
+
+ private DmiExceptionHandler() {
+ }
+
+ /**
+ * Default exception handler.
+ *
+ * @param exception the exception to handle
+ * @return response with response code 500.
+ */
+ @ExceptionHandler
+ public static ResponseEntity<Object> handleInternalServerErrorExceptions(final Exception exception) {
+ return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception);
+ }
+
+ @ExceptionHandler({ModulesNotFoundException.class, ModuleResourceNotFoundException.class})
+ public static ResponseEntity<Object> handleNotFoundExceptions(final DmiException exception) {
+ return buildErrorResponse(HttpStatus.NOT_FOUND, exception);
+ }
+
+ @ExceptionHandler({CmHandleRegistrationException.class, DmiException.class})
+ public static ResponseEntity<Object> handleAnyOtherDmiExceptions(final DmiException exception) {
+ return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception);
+ }
+
+ private static ResponseEntity<Object> buildErrorResponse(final HttpStatus httpStatus, final Exception exception) {
+ logForNonDmiException(exception);
+ final var errorMessage = new ErrorMessage();
+ errorMessage.setStatus(httpStatus.toString());
+ errorMessage.setMessage(exception.getMessage());
+ errorMessage.setDetails(exception instanceof DmiException ? ((DmiException) exception).getDetails() :
+ "Check logs for details.");
+ return new ResponseEntity<>(errorMessage, httpStatus);
+ }
+
+ private static void logForNonDmiException(final Exception exception) {
+ if (exception.getCause() != null || !(exception instanceof DmiException)) {
+ log.error("Exception occurred", exception);
+ }
+ }
+} \ No newline at end of file
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/HttpClientRequestException.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/HttpClientRequestException.java
new file mode 100644
index 00000000..b4b0249f
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/HttpClientRequestException.java
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
+@Getter
+public class HttpClientRequestException extends DmiException {
+
+ private static final long serialVersionUID = 881438585188332404L;
+
+ private final HttpStatus httpStatus;
+
+ /**
+ * Constructor.
+ *
+ * @param cmHandle cmHandle identifier
+ * @param details response body from the client available as details
+ * @param httpStatus http status from the client
+ */
+ public HttpClientRequestException(final String cmHandle, final String details, final HttpStatus httpStatus) {
+ super("Resource data request failed for CM Handle: " + cmHandle, details);
+ this.httpStatus = httpStatus;
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/InvalidDatastoreException.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/InvalidDatastoreException.java
new file mode 100644
index 00000000..aa5b0cb7
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/InvalidDatastoreException.java
@@ -0,0 +1,32 @@
+/*
+ * ============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.exception;
+
+public class InvalidDatastoreException extends RuntimeException {
+ /**
+ * Instantiates a new Invalid datastore exception.
+ *
+ * @param message the message
+ */
+ public InvalidDatastoreException(final String message) {
+ super(message);
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/ModuleResourceNotFoundException.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/ModuleResourceNotFoundException.java
new file mode 100644
index 00000000..65db2712
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/ModuleResourceNotFoundException.java
@@ -0,0 +1,38 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.exception;
+
+public class ModuleResourceNotFoundException extends DmiException {
+
+ private static final long serialVersionUID = 4764849097602543408L;
+
+ private static final String ERROR_MESSAGE = "Module resource not found for given cmHandle: ";
+
+ /**
+ * Constructor.
+ *
+ * @param cmHandle the cm handle
+ * @param details the details of the error
+ */
+ public ModuleResourceNotFoundException(final String cmHandle, final String details) {
+ super(ERROR_MESSAGE + cmHandle, details);
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/ModulesNotFoundException.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/ModulesNotFoundException.java
new file mode 100644
index 00000000..ded54d91
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/ModulesNotFoundException.java
@@ -0,0 +1,38 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.exception;
+
+public class ModulesNotFoundException extends DmiException {
+
+ private static final long serialVersionUID = 980438585188332404L;
+
+ private static final String ERROR_MESSAGE = "Not able to register the given cm-handles: ";
+
+ /**
+ * Constructor.
+ *
+ * @param cmHandle cmHandle identifier
+ * @param details the error details
+ */
+ public ModulesNotFoundException(final String cmHandle, final String details) {
+ super(ERROR_MESSAGE + cmHandle, details);
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/SdncException.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/SdncException.java
new file mode 100644
index 00000000..a83485a9
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/exception/SdncException.java
@@ -0,0 +1,49 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 Bell Canada
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.exception;
+
+import org.springframework.http.HttpStatus;
+
+/*
+Use this exception when SDNC contract fails
+ */
+public class SdncException extends DmiException {
+
+ private static final long serialVersionUID = -2076096996672060566L;
+
+ /**
+ * Constructor.
+ *
+ * @param message message
+ * @param httpStatus httpStatus
+ * @param responseBody responseBody
+ */
+ public SdncException(final String message, final HttpStatus httpStatus, final String responseBody) {
+ super(message, String.format("sdnc http status: %s, response body : %s ",
+ httpStatus.toString(),
+ responseBody));
+ }
+
+ public SdncException(final String message, final String details, final Throwable cause) {
+ super(message, details, cause);
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/AsyncTaskExecutor.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/AsyncTaskExecutor.java
new file mode 100644
index 00000000..6a1c6d1d
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/AsyncTaskExecutor.java
@@ -0,0 +1,121 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.notifications.async;
+
+import com.google.gson.JsonObject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.exception.HttpClientRequestException;
+import org.onap.cps.ncmp.dmi.model.DataAccessRequest;
+import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AsyncTaskExecutor {
+
+ private final DmiAsyncRequestResponseEventProducer dmiAsyncRequestResponseEventProducer;
+
+ private static final DmiAsyncRequestResponseEventCreator dmiAsyncRequestResponseEventCreator =
+ new DmiAsyncRequestResponseEventCreator();
+
+ private static final Map<DataAccessRequest.OperationEnum, HttpStatus> operationToHttpStatusMap = new HashMap<>(6);
+
+ static {
+ operationToHttpStatusMap.put(null, HttpStatus.OK);
+ operationToHttpStatusMap.put(DataAccessRequest.OperationEnum.READ, HttpStatus.OK);
+ operationToHttpStatusMap.put(DataAccessRequest.OperationEnum.CREATE, HttpStatus.CREATED);
+ operationToHttpStatusMap.put(DataAccessRequest.OperationEnum.PATCH, HttpStatus.OK);
+ operationToHttpStatusMap.put(DataAccessRequest.OperationEnum.UPDATE, HttpStatus.OK);
+ operationToHttpStatusMap.put(DataAccessRequest.OperationEnum.DELETE, HttpStatus.NO_CONTENT);
+ }
+
+ /**
+ * Execute task asynchronously and publish response to supplied topic.
+ *
+ * @param taskSupplier functional method is get() task need to executed asynchronously
+ * @param topicName topic name where message need to be published
+ * @param requestId unique requestId for async request
+ * @param operation the operation performed
+ * @param timeOutInMilliSeconds task timeout in milliseconds
+ */
+ public void executeAsyncTask(final Supplier<String> taskSupplier,
+ final String topicName,
+ final String requestId,
+ final DataAccessRequest.OperationEnum operation,
+ final int timeOutInMilliSeconds) {
+ CompletableFuture.supplyAsync(taskSupplier::get)
+ .orTimeout(timeOutInMilliSeconds, TimeUnit.MILLISECONDS)
+ .whenCompleteAsync((resourceDataAsJson, throwable) -> {
+ if (throwable == null) {
+ final String status = operationToHttpStatusMap.get(operation).getReasonPhrase();
+ final String code = String.valueOf(operationToHttpStatusMap.get(operation).value());
+ publishAsyncEvent(topicName, requestId, resourceDataAsJson, status, code);
+ } else {
+ log.error("Error occurred with async request {}", throwable.getMessage());
+ publishAsyncFailureEvent(topicName, requestId, throwable);
+ }
+ });
+ log.info("Async task completed.");
+ }
+
+ private void publishAsyncEvent(final String topicName,
+ final String requestId,
+ final String resourceDataAsJson,
+ final String status,
+ final String code) {
+ final DmiAsyncRequestResponseEvent cpsAsyncRequestResponseEvent =
+ dmiAsyncRequestResponseEventCreator.createEvent(resourceDataAsJson, topicName, requestId, status, code);
+
+ dmiAsyncRequestResponseEventProducer.sendMessage(requestId, cpsAsyncRequestResponseEvent);
+ }
+
+ private void publishAsyncFailureEvent(final String topicName,
+ final String requestId,
+ final Throwable throwable) {
+ HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
+
+ if (throwable instanceof HttpClientRequestException) {
+ final HttpClientRequestException httpClientRequestException = (HttpClientRequestException) throwable;
+ httpStatus = httpClientRequestException.getHttpStatus();
+ }
+
+ final JsonObject errorDetails = new JsonObject();
+ errorDetails.addProperty("errorDetails", throwable.getMessage());
+ publishAsyncEvent(
+ topicName,
+ requestId,
+ errorDetails.toString(),
+ httpStatus.getReasonPhrase(),
+ String.valueOf(httpStatus.value())
+ );
+ }
+}
+
+
+
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/DmiAsyncRequestResponseEventCreator.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/DmiAsyncRequestResponseEventCreator.java
new file mode 100644
index 00000000..1e6c84b1
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/DmiAsyncRequestResponseEventCreator.java
@@ -0,0 +1,90 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.notifications.async;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.UUID;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.Application;
+import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent;
+import org.onap.cps.ncmp.event.model.EventContent;
+
+/**
+ * Helper to create DmiAsyncRequestResponseEvent.
+ */
+@Slf4j
+public class DmiAsyncRequestResponseEventCreator {
+
+ private static final DateTimeFormatter dateTimeFormatter
+ = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ /**
+ * Create an event.
+ *
+ * @param resourceDataAsJson the resource data as json
+ * @param topicParamInQuery the topic to send response to
+ * @param requestId the request id
+ * @param status the status of the request
+ * @param code the code of the response
+ *
+ * @return DmiAsyncRequestResponseEvent
+ */
+ public DmiAsyncRequestResponseEvent createEvent(final String resourceDataAsJson,
+ final String topicParamInQuery,
+ final String requestId,
+ final String status,
+ final String code) {
+ final DmiAsyncRequestResponseEvent dmiAsyncRequestResponseEvent = new DmiAsyncRequestResponseEvent();
+
+ dmiAsyncRequestResponseEvent.setEventId(UUID.randomUUID().toString());
+ dmiAsyncRequestResponseEvent.setEventCorrelationId(requestId);
+ dmiAsyncRequestResponseEvent.setEventType(DmiAsyncRequestResponseEvent.class.getName());
+ dmiAsyncRequestResponseEvent.setEventSchema("urn:cps:" + DmiAsyncRequestResponseEvent.class.getName());
+ dmiAsyncRequestResponseEvent.setEventSchemaVersion("v1");
+ dmiAsyncRequestResponseEvent.setEventSource(Application.class.getPackageName());
+ dmiAsyncRequestResponseEvent.setEventTarget(topicParamInQuery);
+ dmiAsyncRequestResponseEvent.setEventTime(ZonedDateTime.now().format(dateTimeFormatter));
+ dmiAsyncRequestResponseEvent.setEventContent(getEventContent(resourceDataAsJson, status, code));
+
+ return dmiAsyncRequestResponseEvent;
+ }
+
+ @SneakyThrows
+ private EventContent getEventContent(final String resourceDataAsJson, final String status, final String code) {
+ final EventContent eventContent = new EventContent();
+
+ eventContent.setResponseDataSchema("urn:cps:" + DmiAsyncRequestResponseEvent.class.getName() + ":v1");
+ eventContent.setResponseStatus(status);
+ eventContent.setResponseCode(code);
+
+ eventContent.setAdditionalProperty("response-data",
+ objectMapper.readValue(resourceDataAsJson, HashMap.class));
+
+ return eventContent;
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/DmiAsyncRequestResponseEventProducer.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/DmiAsyncRequestResponseEventProducer.java
new file mode 100644
index 00000000..00fea330
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/async/DmiAsyncRequestResponseEventProducer.java
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.notifications.async;
+
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DmiAsyncRequestResponseEventProducer {
+
+ private final KafkaTemplate<String, DmiAsyncRequestResponseEvent> kafkaTemplate;
+
+ @Value("${app.ncmp.async.topic}")
+ private String dmiNcmpTopic;
+
+ /**
+ * Sends message to the configured topic with a message key.
+ *
+ * @param requestId the request id
+ * @param dmiAsyncRequestResponseEvent the event to publish
+ */
+ public void sendMessage(final String requestId, final DmiAsyncRequestResponseEvent dmiAsyncRequestResponseEvent) {
+ kafkaTemplate.send(dmiNcmpTopic, requestId, dmiAsyncRequestResponseEvent);
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcCloudEventCreator.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcCloudEventCreator.java
new file mode 100644
index 00000000..b8bd277d
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcCloudEventCreator.java
@@ -0,0 +1,104 @@
+/*
+ * ============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.notifications.avc;
+
+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.time.format.DateTimeFormatter;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.events.avc1_0_0.AvcEvent;
+import org.onap.cps.ncmp.events.avc1_0_0.Data;
+import org.onap.cps.ncmp.events.avc1_0_0.DatastoreChanges;
+import org.onap.cps.ncmp.events.avc1_0_0.Edit;
+import org.onap.cps.ncmp.events.avc1_0_0.IetfYangPatchYangPatch;
+import org.onap.cps.ncmp.events.avc1_0_0.PushChangeUpdate;
+import org.onap.cps.ncmp.events.avc1_0_0.Value;
+
+/**
+ * Helper to create AvcEvents.
+ */
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class DmiDataAvcCloudEventCreator {
+
+ private static final DateTimeFormatter dateTimeFormatter =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ /**
+ * Creates CloudEvent for DMI Data AVC.
+ *
+ * @param eventCorrelationId correlationid
+ * @return Cloud Event
+ */
+ public static CloudEvent createCloudEvent(final String eventCorrelationId) {
+
+ CloudEvent cloudEvent = null;
+
+ try {
+ cloudEvent = CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withSource(URI.create("NCMP"))
+ .withType(AvcEvent.class.getName())
+ .withDataSchema(URI.create("urn:cps:" + AvcEvent.class.getName() + ":1.0.0"))
+ .withExtension("correlationid", eventCorrelationId)
+ .withData(objectMapper.writeValueAsBytes(createDmiDataAvcEvent())).build();
+ } catch (final JsonProcessingException jsonProcessingException) {
+ log.error("Unable to convert object to json : {}", jsonProcessingException.getMessage());
+ }
+
+ return cloudEvent;
+ }
+
+ private static AvcEvent createDmiDataAvcEvent() {
+ final AvcEvent avcEvent = new AvcEvent();
+ final Data data = new Data();
+ final PushChangeUpdate pushChangeUpdate = new PushChangeUpdate();
+ final DatastoreChanges datastoreChanges = new DatastoreChanges();
+ final IetfYangPatchYangPatch ietfYangPatchYangPatch = new IetfYangPatchYangPatch();
+ ietfYangPatchYangPatch.setPatchId("abcd");
+ final Edit edit1 = new Edit();
+ final Value value = new Value();
+ final Map<String, Object> attributeMap = new LinkedHashMap<>();
+ attributeMap.put("isHoAllowed", false);
+ value.setAttributes(List.of(attributeMap));
+ edit1.setEditId("editId");
+ edit1.setOperation("replace");
+ edit1.setTarget("target_xpath");
+ edit1.setValue(value);
+ ietfYangPatchYangPatch.setEdit(List.of(edit1));
+ datastoreChanges.setIetfYangPatchYangPatch(ietfYangPatchYangPatch);
+ pushChangeUpdate.setDatastoreChanges(datastoreChanges);
+ data.setPushChangeUpdate(pushChangeUpdate);
+
+ avcEvent.setData(data);
+ return avcEvent;
+ }
+
+} \ No newline at end of file
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcEventProducer.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcEventProducer.java
new file mode 100644
index 00000000..075dcf20
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcEventProducer.java
@@ -0,0 +1,50 @@
+/*
+ * ============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.notifications.avc;
+
+
+import io.cloudevents.CloudEvent;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class DmiDataAvcEventProducer {
+
+ private final KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate;
+
+ /**
+ * Publishing DMI Data AVC event payload as CloudEvent.
+ *
+ * @param requestId the request id
+ * @param cloudAvcEvent event with data as DMI DataAVC event
+ */
+ public void publishDmiDataAvcCloudEvent(final String requestId, final CloudEvent cloudAvcEvent) {
+ final ProducerRecord<String, CloudEvent> producerRecord =
+ new ProducerRecord<>("dmi-cm-events", requestId, cloudAvcEvent);
+ cloudEventKafkaTemplate.send(producerRecord);
+ log.debug("AVC event sent");
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcEventSimulationController.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcEventSimulationController.java
new file mode 100644
index 00000000..c5fb8fbe
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/avc/DmiDataAvcEventSimulationController.java
@@ -0,0 +1,61 @@
+/*
+ * ============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.notifications.avc;
+
+import io.cloudevents.CloudEvent;
+import java.util.UUID;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@RequestMapping("${rest.api.dmi-base-path}")
+@RestController
+@Slf4j
+@RequiredArgsConstructor
+public class DmiDataAvcEventSimulationController {
+
+ private final DmiDataAvcEventProducer dmiDataAvcEventProducer;
+
+ /**
+ * Simulate Event for AVC.
+ *
+ * @param numberOfSimulatedEvents number of events to be generated
+ * @return ResponseEntity
+ */
+ @GetMapping(path = "/v1/simulateDmiDataEvent")
+ public ResponseEntity<Void> simulateEvents(
+ @RequestParam("numberOfSimulatedEvents") final Integer numberOfSimulatedEvents) {
+
+ for (int i = 0; i < numberOfSimulatedEvents; i++) {
+ final String eventCorrelationId = UUID.randomUUID().toString();
+ final CloudEvent cloudEvent = DmiDataAvcCloudEventCreator.createCloudEvent(eventCorrelationId);
+ dmiDataAvcEventProducer.publishDmiDataAvcCloudEvent(eventCorrelationId, cloudEvent);
+ }
+
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumer.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumer.java
new file mode 100644
index 00000000..3a9838b0
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiInEventConsumer.java
@@ -0,0 +1,108 @@
+/*
+ * ============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.dmi.notifications.cmsubscription;
+
+import io.cloudevents.CloudEvent;
+import lombok.RequiredArgsConstructor;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.onap.cps.ncmp.dmi.notifications.cmsubscription.model.CmNotificationSubscriptionStatus;
+import org.onap.cps.ncmp.dmi.notifications.mapper.CloudEventMapper;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.CmNotificationSubscriptionDmiOutEvent;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.Data;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class CmNotificationSubscriptionDmiInEventConsumer {
+
+
+ @Value("${app.dmi.avc.cm-subscription-dmi-out}")
+ private String cmNotificationSubscriptionDmiOutTopic;
+ @Value("${dmi.service.name}")
+ private String dmiName;
+ private final KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate;
+
+ /**
+ * Consume the cmNotificationSubscriptionDmiInCloudEvent event.
+ *
+ * @param cmNotificationSubscriptionDmiInCloudEvent the event to be consumed
+ */
+ @KafkaListener(topics = "${app.dmi.avc.cm-subscription-dmi-in}",
+ containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory")
+ public void consumeCmNotificationSubscriptionDmiInEvent(
+ final ConsumerRecord<String, CloudEvent> cmNotificationSubscriptionDmiInCloudEvent) {
+ final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent =
+ CloudEventMapper.toTargetEvent(cmNotificationSubscriptionDmiInCloudEvent.value(),
+ CmNotificationSubscriptionDmiInEvent.class);
+ if (cmNotificationSubscriptionDmiInEvent != null) {
+ final String subscriptionId = cmNotificationSubscriptionDmiInCloudEvent.value().getId();
+ final String subscriptionType = cmNotificationSubscriptionDmiInCloudEvent.value().getType();
+ final String correlationId = String.valueOf(cmNotificationSubscriptionDmiInCloudEvent.value()
+ .getExtension("correlationid"));
+
+ if ("subscriptionCreateRequest".equals(subscriptionType)) {
+ createAndSendCmNotificationSubscriptionDmiOutEvent(subscriptionId, "subscriptionCreateResponse",
+ correlationId, CmNotificationSubscriptionStatus.ACCEPTED);
+ } else if ("subscriptionDeleteRequest".equals(subscriptionType)) {
+ createAndSendCmNotificationSubscriptionDmiOutEvent(subscriptionId, "subscriptionDeleteResponse",
+ correlationId, CmNotificationSubscriptionStatus.ACCEPTED);
+ }
+ }
+ }
+
+ /**
+ * Create Dmi out event object and send to response topic.
+ *
+ * @param eventKey the events key
+ * @param subscriptionType the subscriptions type
+ * @param correlationId the events correlation Id
+ * @param cmNotificationSubscriptionStatus subscriptions status accepted/rejected
+ */
+ public void createAndSendCmNotificationSubscriptionDmiOutEvent(
+ final String eventKey, final String subscriptionType, final String correlationId,
+ final CmNotificationSubscriptionStatus cmNotificationSubscriptionStatus) {
+
+ final CmNotificationSubscriptionDmiOutEvent cmNotificationSubscriptionDmiOutEvent =
+ new CmNotificationSubscriptionDmiOutEvent();
+ final Data cmNotificationSubscriptionDmiOutEventData = new Data();
+
+ if (cmNotificationSubscriptionStatus.equals(CmNotificationSubscriptionStatus.ACCEPTED)) {
+ cmNotificationSubscriptionDmiOutEventData.setStatusCode("1");
+ cmNotificationSubscriptionDmiOutEventData.setStatusMessage("ACCEPTED");
+ } else {
+ cmNotificationSubscriptionDmiOutEventData.setStatusCode("104");
+ cmNotificationSubscriptionDmiOutEventData.setStatusMessage("REJECTED");
+ }
+ cmNotificationSubscriptionDmiOutEvent.setData(cmNotificationSubscriptionDmiOutEventData);
+
+ cloudEventKafkaTemplate.send(cmNotificationSubscriptionDmiOutTopic, eventKey,
+ CmNotificationSubscriptionDmiOutEventToCloudEventMapper.toCloudEvent(cmNotificationSubscriptionDmiOutEvent,
+ subscriptionType, dmiName, correlationId));
+
+ }
+
+
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiOutEventToCloudEventMapper.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiOutEventToCloudEventMapper.java
new file mode 100644
index 00000000..51205da2
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/CmNotificationSubscriptionDmiOutEventToCloudEventMapper.java
@@ -0,0 +1,62 @@
+/*
+ * ============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.dmi.notifications.cmsubscription;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.cloudevents.CloudEvent;
+import io.cloudevents.core.builder.CloudEventBuilder;
+import java.net.URI;
+import java.util.UUID;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.onap.cps.ncmp.dmi.exception.CloudEventConstructionException;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.CmNotificationSubscriptionDmiOutEvent;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class CmNotificationSubscriptionDmiOutEventToCloudEventMapper {
+
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ /**
+ * Maps SubscriptionEventResponse to a CloudEvent.
+ *
+ * @param cmSubscriptionDmiOutEvent object.
+ * @param subscriptionType String of subscription type.
+ * @param dmiName String of dmiName.
+ * @param correlationId String of correlationId.
+ * @return CloudEvent built.
+ */
+ public static CloudEvent toCloudEvent(final CmNotificationSubscriptionDmiOutEvent cmSubscriptionDmiOutEvent,
+ final String subscriptionType, final String dmiName,
+ final String correlationId) {
+ try {
+ return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withSource(URI.create(dmiName))
+ .withType(subscriptionType)
+ .withDataSchema(URI.create("urn:cps:org.onap.ncmp.dmi.cm.subscription:1.0.0"))
+ .withExtension("correlationid", correlationId)
+ .withData(objectMapper.writeValueAsBytes(cmSubscriptionDmiOutEvent)).build();
+ } catch (final Exception ex) {
+ throw new CloudEventConstructionException("The Cloud Event could not be constructed",
+ "Invalid object passed", ex);
+ }
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/model/CmNotificationSubscriptionStatus.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/model/CmNotificationSubscriptionStatus.java
new file mode 100644
index 00000000..40b1297f
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/cmsubscription/model/CmNotificationSubscriptionStatus.java
@@ -0,0 +1,32 @@
+/*
+ * ============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.dmi.notifications.cmsubscription.model;
+
+public enum CmNotificationSubscriptionStatus {
+
+ ACCEPTED("ACCEPTED"), REJECTED("REJECTED");
+
+ private final String cmNotificationSubscriptionStatusValue;
+
+ CmNotificationSubscriptionStatus(final String cmNotificationSubscriptionStatusValue) {
+ this.cmNotificationSubscriptionStatusValue = cmNotificationSubscriptionStatusValue;
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/mapper/CloudEventMapper.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/mapper/CloudEventMapper.java
new file mode 100644
index 00000000..8f196cfc
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/notifications/mapper/CloudEventMapper.java
@@ -0,0 +1,62 @@
+/*
+ * ============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.dmi.notifications.mapper;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.cloudevents.CloudEvent;
+import io.cloudevents.core.CloudEventUtils;
+import io.cloudevents.core.data.PojoCloudEventData;
+import io.cloudevents.jackson.PojoCloudEventDataMapper;
+import io.cloudevents.rw.CloudEventRWException;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class CloudEventMapper {
+
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ /**
+ * Generic method to map cloud event data to target event class object.
+ *
+ * @param cloudEvent input cloud event
+ * @param targetEventClass target event class
+ * @param <T> target event class type
+ * @return mapped target event
+ */
+ public static <T> T toTargetEvent(final CloudEvent cloudEvent, final Class<T> targetEventClass) {
+ PojoCloudEventData<T> mappedCloudEvent = null;
+
+ try {
+ mappedCloudEvent =
+ CloudEventUtils.mapData(cloudEvent, PojoCloudEventDataMapper.from(objectMapper, targetEventClass));
+
+ } catch (final CloudEventRWException cloudEventRwException) {
+ log.error("Unable to map cloud event to target event class type : {} with cause : {}", targetEventClass,
+ cloudEventRwException.getMessage());
+ }
+
+ return mappedCloudEvent == null ? null : mappedCloudEvent.getValue();
+ }
+
+} \ No newline at end of file
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java
new file mode 100644
index 00000000..cad5e726
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java
@@ -0,0 +1,253 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2023 Nordix Foundation
+ * Modifications Copyright (C) 2022 Bell Canada
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.controller;
+
+import static org.onap.cps.ncmp.dmi.model.DataAccessRequest.OperationEnum;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.model.CmHandles;
+import org.onap.cps.ncmp.dmi.model.DataAccessRequest;
+import org.onap.cps.ncmp.dmi.model.ModuleReferencesRequest;
+import org.onap.cps.ncmp.dmi.model.ModuleResourcesReadRequest;
+import org.onap.cps.ncmp.dmi.model.ModuleSet;
+import org.onap.cps.ncmp.dmi.model.ResourceDataOperationRequests;
+import org.onap.cps.ncmp.dmi.model.YangResources;
+import org.onap.cps.ncmp.dmi.notifications.async.AsyncTaskExecutor;
+import org.onap.cps.ncmp.dmi.rest.api.DmiPluginApi;
+import org.onap.cps.ncmp.dmi.rest.api.DmiPluginInternalApi;
+import org.onap.cps.ncmp.dmi.rest.controller.handlers.DatastoreType;
+import org.onap.cps.ncmp.dmi.service.DmiService;
+import org.onap.cps.ncmp.dmi.service.model.ModuleReference;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequestMapping("${rest.api.dmi-base-path}")
+@RestController
+@Slf4j
+@RequiredArgsConstructor
+public class DmiRestController implements DmiPluginApi, DmiPluginInternalApi {
+
+ private final DmiService dmiService;
+ private final ObjectMapper objectMapper;
+ private final AsyncTaskExecutor asyncTaskExecutor;
+ private static final Map<OperationEnum, HttpStatus> operationToHttpStatusMap = new HashMap<>(6);
+
+ @Value("${notification.async.executor.time-out-value-in-ms:2000}")
+ private int timeOutInMillis;
+
+ static {
+ operationToHttpStatusMap.put(null, HttpStatus.OK);
+ operationToHttpStatusMap.put(OperationEnum.READ, HttpStatus.OK);
+ operationToHttpStatusMap.put(OperationEnum.CREATE, HttpStatus.CREATED);
+ operationToHttpStatusMap.put(OperationEnum.PATCH, HttpStatus.OK);
+ operationToHttpStatusMap.put(OperationEnum.UPDATE, HttpStatus.OK);
+ operationToHttpStatusMap.put(OperationEnum.DELETE, HttpStatus.NO_CONTENT);
+ }
+
+ @Override
+ public ResponseEntity<ModuleSet> getModuleReferences(final String cmHandle,
+ final ModuleReferencesRequest body) {
+ // For onap-dmi-plugin we don't need cmHandleProperties, so DataAccessReadRequest is not used.
+ final ModuleSet moduleSet = dmiService.getModulesForCmHandle(cmHandle);
+ return ResponseEntity.ok(moduleSet);
+ }
+
+ @Override
+ public ResponseEntity<YangResources> retrieveModuleResources(
+ final String cmHandle,
+ final ModuleResourcesReadRequest moduleResourcesReadRequest) {
+ final List<ModuleReference> moduleReferences = convertRestObjectToJavaApiObject(moduleResourcesReadRequest);
+ final YangResources yangResources = dmiService.getModuleResources(cmHandle, moduleReferences);
+ log.info("Module set tag received: {}", moduleResourcesReadRequest.getModuleSetTag());
+ return new ResponseEntity<>(yangResources, HttpStatus.OK);
+ }
+
+ /**
+ * This method register given list of cm-handles to ncmp.
+ *
+ * @param cmHandles list of cm-handles
+ * @return (@ code ResponseEntity) response entity
+ */
+ public ResponseEntity<String> registerCmHandles(final CmHandles cmHandles) {
+ final List<String> cmHandlesList = cmHandles.getCmHandles();
+ if (cmHandlesList.isEmpty()) {
+ return new ResponseEntity<>("Need at least one cmHandle to process.", HttpStatus.BAD_REQUEST);
+ }
+ dmiService.registerCmHandles(cmHandlesList);
+ return new ResponseEntity<>("cm-handle registered successfully.", HttpStatus.CREATED);
+ }
+
+ /**
+ * 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 resourceDataOperationRequests list of operation details
+ * @return (@ code ResponseEntity) response entity
+ */
+ @Override
+ public ResponseEntity<Void> getResourceDataForCmHandleDataOperation(final String topic, final String requestId,
+ final ResourceDataOperationRequests resourceDataOperationRequests) {
+ log.info("Request Details (for testing purposes)");
+ log.info("Request Id: {}", requestId);
+ log.info("Topic: {}", topic);
+
+ log.info("Details of the first Operation");
+ log.info("Resource Identifier: {}", resourceDataOperationRequests.get(0).getResourceIdentifier());
+ log.info("Module Set Tag: {}", resourceDataOperationRequests.get(0).getCmHandles().get(0).getModuleSetTag());
+ log.info("Operation Id: {}", resourceDataOperationRequests.get(0).getOperationId());
+ log.info("Cm Handles: {}", resourceDataOperationRequests.get(0).getCmHandles());
+ log.info("Options: {}", resourceDataOperationRequests.get(0).getOptions());
+
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ /**
+ * This method fetches the resource for given cm handle using pass through operational or running datastore.
+ * It filters the response on the basis of options query parameters and returns response. Passthrough Running
+ * supports both read and write operation whereas passthrough operational does not support write operations.
+ *
+ * @param datastoreName name of the datastore
+ * @param cmHandle cm handle identifier
+ * @param resourceIdentifier resource identifier to fetch data
+ * @param optionsParamInQuery options query parameter
+ * @param topicParamInQuery topic name for (triggering) async responses
+ * @param dataAccessRequest data Access Request
+ * @return {@code ResponseEntity} response entity
+ */
+ @Override
+ public ResponseEntity<Object> dataAccessPassthrough(final String datastoreName,
+ final String cmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final DataAccessRequest dataAccessRequest) {
+ log.info("Module set tag: {}", dataAccessRequest.getModuleSetTag());
+ if (DatastoreType.PASSTHROUGH_OPERATIONAL == DatastoreType.fromDatastoreName(datastoreName)) {
+ return dataAccessPassthroughOperational(resourceIdentifier, cmHandle, dataAccessRequest,
+ optionsParamInQuery, topicParamInQuery);
+ }
+ return dataAccessPassthroughRunning(resourceIdentifier, cmHandle, dataAccessRequest,
+ optionsParamInQuery, topicParamInQuery);
+ }
+
+ private ResponseEntity<Object> dataAccessPassthroughOperational(final String resourceIdentifier,
+ final String cmHandle,
+ final DataAccessRequest dataAccessRequest,
+ final String optionsParamInQuery,
+ final String topicParamInQuery) {
+ if (isReadOperation(dataAccessRequest)) {
+ if (hasTopic(topicParamInQuery)) {
+ return handleAsyncRequest(resourceIdentifier, cmHandle, dataAccessRequest, optionsParamInQuery,
+ topicParamInQuery);
+ }
+
+ final String resourceDataAsJson = dmiService.getResourceData(cmHandle, resourceIdentifier,
+ optionsParamInQuery, DmiService.RESTCONF_CONTENT_PASSTHROUGH_OPERATIONAL_QUERY_PARAM);
+ return ResponseEntity.ok(resourceDataAsJson);
+ }
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+
+ private ResponseEntity<Object> dataAccessPassthroughRunning(final String resourceIdentifier,
+ final String cmHandle,
+ final DataAccessRequest dataAccessRequest,
+ final String optionsParamInQuery,
+ final String topicParamInQuery) {
+ if (hasTopic(topicParamInQuery)) {
+ asyncTaskExecutor.executeAsyncTask(() ->
+ getSdncResponseForPassThroughRunning(
+ resourceIdentifier,
+ cmHandle,
+ dataAccessRequest,
+ optionsParamInQuery),
+ topicParamInQuery,
+ dataAccessRequest.getRequestId(),
+ dataAccessRequest.getOperation(),
+ timeOutInMillis
+ );
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+
+ final String sdncResponse =
+ getSdncResponseForPassThroughRunning(resourceIdentifier, cmHandle, dataAccessRequest, optionsParamInQuery);
+ return new ResponseEntity<>(sdncResponse, operationToHttpStatusMap.get(dataAccessRequest.getOperation()));
+ }
+
+ private String getSdncResponseForPassThroughRunning(final String resourceIdentifier,
+ final String cmHandle,
+ final DataAccessRequest dataAccessRequest,
+ final String optionsParamInQuery) {
+ if (isReadOperation(dataAccessRequest)) {
+ return dmiService.getResourceData(cmHandle, resourceIdentifier, optionsParamInQuery,
+ DmiService.RESTCONF_CONTENT_PASSTHROUGH_RUNNING_QUERY_PARAM);
+ }
+
+ return dmiService.writeData(dataAccessRequest.getOperation(), cmHandle, resourceIdentifier,
+ dataAccessRequest.getDataType(), dataAccessRequest.getData());
+ }
+
+ private boolean isReadOperation(final DataAccessRequest dataAccessRequest) {
+ return dataAccessRequest.getOperation() == null
+ || dataAccessRequest.getOperation().equals(DataAccessRequest.OperationEnum.READ);
+ }
+
+ private List<ModuleReference> convertRestObjectToJavaApiObject(
+ final ModuleResourcesReadRequest moduleResourcesReadRequest) {
+ return objectMapper
+ .convertValue(moduleResourcesReadRequest.getData().getModules(),
+ new TypeReference<List<ModuleReference>>() {});
+ }
+
+ private boolean hasTopic(final String topicParamInQuery) {
+ return !(topicParamInQuery == null || topicParamInQuery.isBlank());
+ }
+
+ private ResponseEntity<Object> handleAsyncRequest(final String resourceIdentifier,
+ final String cmHandle,
+ final DataAccessRequest dataAccessRequest,
+ final String optionsParamInQuery,
+ final String topicParamInQuery) {
+ asyncTaskExecutor.executeAsyncTask(() ->
+ dmiService.getResourceData(
+ cmHandle,
+ resourceIdentifier,
+ optionsParamInQuery,
+ DmiService.RESTCONF_CONTENT_PASSTHROUGH_OPERATIONAL_QUERY_PARAM),
+ topicParamInQuery,
+ dataAccessRequest.getRequestId(),
+ dataAccessRequest.getOperation(),
+ timeOutInMillis
+ );
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/handlers/DatastoreType.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/handlers/DatastoreType.java
new file mode 100644
index 00000000..3f280403
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/handlers/DatastoreType.java
@@ -0,0 +1,65 @@
+/*
+ * ============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.controller.handlers;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Getter;
+import org.onap.cps.ncmp.dmi.exception.InvalidDatastoreException;
+
+@Getter
+public enum DatastoreType {
+
+ PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running"),
+ PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational");
+
+ DatastoreType(final String datastoreName) {
+ this.datastoreName = datastoreName;
+ }
+
+ private final String datastoreName;
+ private static final Map<String, DatastoreType> datastoreNameToDatastoreType = new HashMap<>();
+
+ static {
+ Arrays.stream(DatastoreType.values()).forEach(
+ type -> datastoreNameToDatastoreType.put(type.getDatastoreName(), type));
+ }
+
+ /**
+ * From datastore name get datastore type.
+ *
+ * @param datastoreName the datastore name
+ * @return the datastore type
+ */
+ public static DatastoreType fromDatastoreName(final String datastoreName) {
+
+ final DatastoreType datastoreType = datastoreNameToDatastoreType.get(datastoreName);
+
+ if (null == datastoreType) {
+ throw new InvalidDatastoreException(datastoreName + " is an invalid datastore name");
+ }
+
+ return datastoreType;
+ }
+
+}
+
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java
new file mode 100644
index 00000000..f0826a81
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java
@@ -0,0 +1,95 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2023 Nordix Foundation
+ * Modifications Copyright (C) 2022 Bell Canada
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.service;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import org.onap.cps.ncmp.dmi.exception.DmiException;
+import org.onap.cps.ncmp.dmi.model.DataAccessRequest;
+import org.onap.cps.ncmp.dmi.model.ModuleSet;
+import org.onap.cps.ncmp.dmi.model.YangResources;
+import org.onap.cps.ncmp.dmi.service.model.ModuleReference;
+
+
+
+/**
+ * Interface for handling Dmi plugin Data.
+ */
+public interface DmiService {
+
+ String RESTCONF_CONTENT_PASSTHROUGH_OPERATIONAL_QUERY_PARAM = "content=all";
+ String RESTCONF_CONTENT_PASSTHROUGH_RUNNING_QUERY_PARAM = "content=config";
+
+ /**
+ * This method fetches all modules for given Cm Handle.
+ *
+ * @param cmHandle cm-handle to fetch the modules information
+ * @return {@code String} returns all modules
+ * @throws DmiException can throw dmi exception
+ */
+ ModuleSet getModulesForCmHandle(String cmHandle) throws DmiException;
+
+ /**
+ * This method used to register the given {@code CmHandles} which contains list of {@code CmHandle} to cps
+ * repository.
+ *
+ * @param cmHandles list of cm-handles
+ */
+ void registerCmHandles(List<String> cmHandles);
+
+ /**
+ * Get module resources for the given cm handle and modules.
+ *
+ * @param cmHandle cmHandle
+ * @param modules a list of module data
+ * @return returns all yang resources
+ */
+ YangResources getModuleResources(String cmHandle, List<ModuleReference> modules);
+
+ /**
+ * This method use to fetch the resource data from cm handle for the given datastore and resource
+ * Identifier. Options query parameter are used to filter the response from network resource.
+ *
+ * @param cmHandle cm handle identifier
+ * @param resourceIdentifier resource identifier
+ * @param optionsParamInQuery options query parameter
+ * @param restconfContentQueryParam restconf content i.e. datastore to use
+ * @return {@code Object} response from network function
+ */
+ String getResourceData(@NotNull String cmHandle,
+ @NotNull String resourceIdentifier,
+ String optionsParamInQuery,
+ String restconfContentQueryParam);
+
+ /**
+ * Write resource data to sdnc (will default to 'content=config', does not need to be specified).
+ *
+ * @param cmHandle cmHandle
+ * @param resourceIdentifier resource identifier
+ * @param dataType accept header parameter
+ * @param data request data
+ * @return response from sdnc
+ */
+ String writeData(DataAccessRequest.OperationEnum operation, String cmHandle,
+ String resourceIdentifier, String dataType,
+ String data);
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
new file mode 100644
index 00000000..6acbe09b
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
@@ -0,0 +1,196 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2023 Nordix Foundation
+ * Modifications Copyright (C) 2021-2022 Bell Canada
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.service;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.config.DmiPluginConfig.DmiPluginProperties;
+import org.onap.cps.ncmp.dmi.exception.CmHandleRegistrationException;
+import org.onap.cps.ncmp.dmi.exception.DmiException;
+import org.onap.cps.ncmp.dmi.exception.HttpClientRequestException;
+import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException;
+import org.onap.cps.ncmp.dmi.exception.ModulesNotFoundException;
+import org.onap.cps.ncmp.dmi.model.DataAccessRequest;
+import org.onap.cps.ncmp.dmi.model.ModuleSet;
+import org.onap.cps.ncmp.dmi.model.ModuleSetSchemasInner;
+import org.onap.cps.ncmp.dmi.model.YangResource;
+import org.onap.cps.ncmp.dmi.model.YangResources;
+import org.onap.cps.ncmp.dmi.service.client.NcmpRestClient;
+import org.onap.cps.ncmp.dmi.service.model.CmHandleOperation;
+import org.onap.cps.ncmp.dmi.service.model.CreatedCmHandle;
+import org.onap.cps.ncmp.dmi.service.model.ModuleReference;
+import org.onap.cps.ncmp.dmi.service.model.ModuleSchema;
+import org.onap.cps.ncmp.dmi.service.operation.SdncOperations;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+public class DmiServiceImpl implements DmiService {
+
+ private SdncOperations sdncOperations;
+ private NcmpRestClient ncmpRestClient;
+ private ObjectMapper objectMapper;
+ private DmiPluginProperties dmiPluginProperties;
+
+ /**
+ * Constructor.
+ *
+ * @param dmiPluginProperties dmiPluginProperties
+ * @param ncmpRestClient ncmpRestClient
+ * @param sdncOperations sdncOperations
+ * @param objectMapper objectMapper
+ */
+ public DmiServiceImpl(final DmiPluginProperties dmiPluginProperties,
+ final NcmpRestClient ncmpRestClient,
+ final SdncOperations sdncOperations, final ObjectMapper objectMapper) {
+ this.dmiPluginProperties = dmiPluginProperties;
+ this.ncmpRestClient = ncmpRestClient;
+ this.objectMapper = objectMapper;
+ this.sdncOperations = sdncOperations;
+ }
+
+ @Override
+ public ModuleSet getModulesForCmHandle(final String cmHandle) throws DmiException {
+ final Collection<ModuleSchema> moduleSchemas = sdncOperations.getModuleSchemasFromNode(cmHandle);
+ if (moduleSchemas.isEmpty()) {
+ throw new ModulesNotFoundException(cmHandle, "SDNC returned no modules for given cm-handle.");
+ } else {
+ final ModuleSet moduleSet = new ModuleSet();
+ moduleSchemas.forEach(moduleSchema ->
+ moduleSet.addSchemasItem(toModuleSetSchemas(moduleSchema)));
+ return moduleSet;
+ }
+ }
+
+ @Override
+ public YangResources getModuleResources(final String cmHandle, final List<ModuleReference> moduleReferences) {
+ final YangResources yangResources = new YangResources();
+ for (final ModuleReference moduleReference : moduleReferences) {
+ final String moduleRequest = createModuleRequest(moduleReference);
+ final ResponseEntity<String> responseEntity = sdncOperations.getModuleResource(cmHandle, moduleRequest);
+ if (responseEntity.getStatusCode() == HttpStatus.OK) {
+ final YangResource yangResource = YangResourceExtractor.toYangResource(moduleReference, responseEntity);
+ yangResources.add(yangResource);
+ } else if (responseEntity.getStatusCode() == HttpStatus.NOT_FOUND) {
+ log.error("SDNC did not return a module resource for the given cmHandle {}", cmHandle);
+ throw new ModuleResourceNotFoundException(cmHandle,
+ "SDNC did not return a module resource for the given cmHandle.");
+ } else {
+ log.error("Error occurred when getting module resources from SDNC for the given cmHandle {}", cmHandle);
+ throw new HttpClientRequestException(
+ cmHandle, responseEntity.getBody(), (HttpStatus) responseEntity.getStatusCode());
+ }
+ }
+ return yangResources;
+ }
+
+ @Override
+ public void registerCmHandles(final List<String> cmHandles) {
+ final CmHandleOperation cmHandleOperation = new CmHandleOperation();
+ cmHandleOperation.setDmiPlugin(dmiPluginProperties.getDmiServiceUrl());
+ final List<CreatedCmHandle> createdCmHandleList = new ArrayList<>();
+ for (final String cmHandle : cmHandles) {
+ final CreatedCmHandle createdCmHandle = new CreatedCmHandle();
+ createdCmHandle.setCmHandle(cmHandle);
+ createdCmHandleList.add(createdCmHandle);
+ }
+ cmHandleOperation.setCreatedCmHandles(createdCmHandleList);
+ final String cmHandlesJson;
+ try {
+ cmHandlesJson = objectMapper.writeValueAsString(cmHandleOperation);
+ } catch (final JsonProcessingException e) {
+ log.error("Parsing error occurred while converting cm-handles to JSON {}", cmHandles);
+ throw new DmiException("Internal Server Error.",
+ "Parsing error occurred while converting given cm-handles object list to JSON ");
+ }
+ final ResponseEntity<String> responseEntity = ncmpRestClient.registerCmHandlesWithNcmp(cmHandlesJson);
+ if (!responseEntity.getStatusCode().is2xxSuccessful()) {
+ throw new CmHandleRegistrationException(responseEntity.getBody());
+ }
+ }
+
+ private ModuleSetSchemasInner toModuleSetSchemas(final ModuleSchema moduleSchema) {
+ final ModuleSetSchemasInner moduleSetSchemas = new ModuleSetSchemasInner();
+ moduleSetSchemas.setModuleName(moduleSchema.getIdentifier());
+ moduleSetSchemas.setNamespace(moduleSchema.getNamespace());
+ moduleSetSchemas.setRevision(moduleSchema.getVersion());
+ return moduleSetSchemas;
+ }
+
+ @Override
+ public String getResourceData(final String cmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String restconfContentQueryParam) {
+ final ResponseEntity<String> responseEntity = sdncOperations.getResouceDataForOperationalAndRunning(cmHandle,
+ resourceIdentifier,
+ optionsParamInQuery,
+ restconfContentQueryParam);
+ return prepareAndSendResponse(responseEntity, cmHandle);
+ }
+
+ @Override
+ public String writeData(final DataAccessRequest.OperationEnum operation,
+ final String cmHandle,
+ final String resourceIdentifier,
+ final String dataType, final String data) {
+ final ResponseEntity<String> responseEntity =
+ sdncOperations.writeData(operation, cmHandle, resourceIdentifier, dataType, data);
+ return prepareAndSendResponse(responseEntity, cmHandle);
+ }
+
+ private String prepareAndSendResponse(final ResponseEntity<String> responseEntity, final String cmHandle) {
+ if (responseEntity.getStatusCode().is2xxSuccessful()) {
+ return responseEntity.getBody();
+ } else {
+ throw new HttpClientRequestException(cmHandle, responseEntity.getBody(),
+ (HttpStatus) responseEntity.getStatusCode());
+ }
+ }
+
+ private String createModuleRequest(final ModuleReference moduleReference) {
+ final Map<String, String> ietfNetconfModuleReferences = new LinkedHashMap<>();
+ ietfNetconfModuleReferences.put("ietf-netconf-monitoring:identifier", moduleReference.getName());
+ ietfNetconfModuleReferences.put("ietf-netconf-monitoring:version", moduleReference.getRevision());
+ final ObjectWriter objectWriter = objectMapper.writer().withRootName("ietf-netconf-monitoring:input");
+ final String moduleRequest;
+ try {
+ moduleRequest = objectWriter.writeValueAsString(ietfNetconfModuleReferences);
+ } catch (final JsonProcessingException e) {
+ log.error("JSON exception occurred when creating the module request for the given module reference {}",
+ moduleReference.getName());
+ throw new DmiException("Unable to process JSON.",
+ "JSON exception occurred when creating the module request.", e);
+ }
+ return moduleRequest;
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/YangResourceExtractor.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/YangResourceExtractor.java
new file mode 100644
index 00000000..d41240b9
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/YangResourceExtractor.java
@@ -0,0 +1,59 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.service;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException;
+import org.onap.cps.ncmp.dmi.model.YangResource;
+import org.onap.cps.ncmp.dmi.service.model.ModuleReference;
+import org.springframework.http.ResponseEntity;
+
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class YangResourceExtractor {
+
+ static YangResource toYangResource(final ModuleReference moduleReference,
+ final ResponseEntity<String> responseEntity) {
+ final YangResource yangResource = new YangResource();
+ yangResource.setModuleName(moduleReference.getName());
+ yangResource.setRevision(moduleReference.getRevision());
+ yangResource.setYangSource(extractYangSourceFromBody(responseEntity));
+ return yangResource;
+ }
+
+ private static String extractYangSourceFromBody(final ResponseEntity<String> responseEntity) {
+ final JsonObject responseBodyAsJsonObject = new Gson().fromJson(responseEntity.getBody(), JsonObject.class);
+ final JsonObject monitoringOutputAsJsonObject =
+ responseBodyAsJsonObject.getAsJsonObject("ietf-netconf-monitoring:output");
+ if (monitoringOutputAsJsonObject == null
+ || monitoringOutputAsJsonObject.getAsJsonPrimitive("data") == null) {
+ log.error("Error occurred when trying to parse the response body from sdnc {}", responseEntity.getBody());
+ throw new ModuleResourceNotFoundException(responseEntity.getBody(),
+ "Error occurred when trying to parse the response body from sdnc.");
+ }
+ return monitoringOutputAsJsonObject.getAsJsonPrimitive("data").getAsString();
+ }
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/client/NcmpRestClient.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/client/NcmpRestClient.java
new file mode 100644
index 00000000..94783f3b
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/client/NcmpRestClient.java
@@ -0,0 +1,64 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.service.client;
+
+import org.onap.cps.ncmp.dmi.config.DmiConfiguration.CpsProperties;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponentsBuilder;
+
+@Component
+public class NcmpRestClient {
+
+ private CpsProperties cpsProperties;
+ private RestTemplate restTemplate;
+
+ public NcmpRestClient(final CpsProperties cpsProperties, final RestTemplate restTemplate) {
+ this.cpsProperties = cpsProperties;
+ this.restTemplate = restTemplate;
+ }
+
+ /**
+ * Register a cmHandle with NCMP using a HTTP call.
+ * @param jsonData json data
+ * @return the response entity
+ */
+ public ResponseEntity<String> registerCmHandlesWithNcmp(final String jsonData) {
+ final var ncmpRegistrationUrl = buildNcmpRegistrationUrl();
+ final var httpHeaders = new HttpHeaders();
+ httpHeaders.setBasicAuth(cpsProperties.getAuthUsername(), cpsProperties.getAuthPassword());
+ httpHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
+ final var httpEntity = new HttpEntity<>(jsonData, httpHeaders);
+ return restTemplate.exchange(ncmpRegistrationUrl, HttpMethod.POST, httpEntity, String.class);
+ }
+
+ private String buildNcmpRegistrationUrl() {
+ return UriComponentsBuilder
+ .fromHttpUrl(cpsProperties.getBaseUrl())
+ .path(cpsProperties.getDmiRegistrationUrl())
+ .toUriString();
+ }
+} \ No newline at end of file
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClient.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClient.java
new file mode 100644
index 00000000..179707ab
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/client/SdncRestconfClient.java
@@ -0,0 +1,87 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.service.client;
+
+import org.onap.cps.ncmp.dmi.config.DmiConfiguration.SdncProperties;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+@Component
+public class SdncRestconfClient {
+
+ private SdncProperties sdncProperties;
+ private RestTemplate restTemplate;
+
+ public SdncRestconfClient(final SdncProperties sdncProperties, final RestTemplate restTemplate) {
+ this.sdncProperties = sdncProperties;
+ this.restTemplate = restTemplate;
+ }
+
+ /**
+ * restconf get operation on sdnc.
+ *
+ * @param getResourceUrl sdnc get url
+ * @return the response entity
+ */
+ public ResponseEntity<String> getOperation(final String getResourceUrl) {
+ return getOperation(getResourceUrl, new HttpHeaders());
+ }
+
+ /**
+ * Overloaded restconf get operation on sdnc with http headers.
+ *
+ * @param getResourceUrl sdnc get url
+ * @param httpHeaders http headers
+ * @return the response entity
+ */
+ public ResponseEntity<String> getOperation(final String getResourceUrl, final HttpHeaders httpHeaders) {
+ return httpOperationWithJsonData(HttpMethod.GET, getResourceUrl, null, httpHeaders);
+ }
+
+ /**
+ * restconf http operations on sdnc.
+ *
+ * @param httpMethod HTTP Method
+ * @param resourceUrl sdnc resource url
+ * @param jsonData json data
+ * @param httpHeaders HTTP Headers
+ * @return response entity
+ */
+ public ResponseEntity<String> httpOperationWithJsonData(final HttpMethod httpMethod,
+ final String resourceUrl,
+ final String jsonData,
+ final HttpHeaders httpHeaders) {
+ final String sdncBaseUrl = sdncProperties.getBaseUrl();
+ final String sdncRestconfUrl = sdncBaseUrl.concat(resourceUrl);
+ httpHeaders.setBasicAuth(sdncProperties.getAuthUsername(), sdncProperties.getAuthPassword());
+ final HttpEntity<String> httpEntity;
+ if (jsonData == null) {
+ httpEntity = new HttpEntity<>(httpHeaders);
+ } else {
+ httpEntity = new HttpEntity<>(jsonData, httpHeaders);
+ }
+ return restTemplate.exchange(sdncRestconfUrl, httpMethod, httpEntity, String.class);
+ }
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/CmHandleOperation.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/CmHandleOperation.java
new file mode 100644
index 00000000..82eac92a
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/CmHandleOperation.java
@@ -0,0 +1,35 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.service.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Getter
+@Setter
+public class CmHandleOperation {
+
+ private String dmiPlugin;
+ private List<CreatedCmHandle> createdCmHandles;
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/CreatedCmHandle.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/CreatedCmHandle.java
new file mode 100644
index 00000000..6ab6a01e
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/CreatedCmHandle.java
@@ -0,0 +1,36 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.service.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Getter
+@Setter
+public class CreatedCmHandle {
+
+ private String cmHandle;
+ private Map<String, String> cmHandleProperties;
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleReference.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleReference.java
new file mode 100644
index 00000000..75c37dff
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleReference.java
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.service.model;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Module Reference.
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ModuleReference {
+
+ private String name;
+ private String revision;
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchema.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchema.java
new file mode 100644
index 00000000..c77e0e41
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/model/ModuleSchema.java
@@ -0,0 +1,36 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 Nordix Foundation
+ * Modifications Copyright (C) 2021 Bell Canada
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.service.model;
+
+import java.util.List;
+import lombok.Data;
+
+@Data
+public class ModuleSchema {
+
+ private String identifier;
+ private String version;
+ private String format;
+ private String namespace;
+ private List<String> location;
+
+}
diff --git a/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java
new file mode 100644
index 00000000..fd94e634
--- /dev/null
+++ b/dmi-service/src/main/java/org/onap/cps/ncmp/dmi/service/operation/SdncOperations.java
@@ -0,0 +1,270 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021-2023 Nordix Foundation
+ * Modifications Copyright (C) 2021-2022 Bell Canada
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.service.operation;
+
+import static org.onap.cps.ncmp.dmi.model.DataAccessRequest.OperationEnum;
+
+import com.jayway.jsonpath.Configuration;
+import com.jayway.jsonpath.JsonPath;
+import com.jayway.jsonpath.JsonPathException;
+import com.jayway.jsonpath.TypeRef;
+import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
+import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.onap.cps.ncmp.dmi.config.DmiConfiguration.SdncProperties;
+import org.onap.cps.ncmp.dmi.exception.SdncException;
+import org.onap.cps.ncmp.dmi.model.DataAccessRequest;
+import org.onap.cps.ncmp.dmi.service.client.SdncRestconfClient;
+import org.onap.cps.ncmp.dmi.service.model.ModuleSchema;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.util.UriComponentsBuilder;
+
+@Component
+public class SdncOperations {
+
+ private static final String TOPOLOGY_URL_TEMPLATE_DATA = "/rests/data/network-topology:network-topology/";
+ private static final String TOPOLOGY_URL_TEMPLATE_OPERATIONAL =
+ "/rests/operations/network-topology:network-topology/";
+ private static final String GET_SCHEMA_URL = "ietf-netconf-monitoring:netconf-state/schemas";
+ private static final String GET_SCHEMA_SOURCES_URL = "/ietf-netconf-monitoring:get-schema";
+ private static final String PATH_TO_MODULE_SCHEMAS = "$.ietf-netconf-monitoring:schemas.schema";
+ private static final int QUERY_PARAM_SPLIT_LIMIT = 2;
+ private static final int QUERY_PARAM_VALUE_INDEX = 1;
+ private static final int QUERY_PARAM_NAME_INDEX = 0;
+
+ private static EnumMap<OperationEnum, HttpMethod> operationToHttpMethodMap = new EnumMap<>(OperationEnum.class);
+
+ static {
+ operationToHttpMethodMap.put(OperationEnum.READ, HttpMethod.GET);
+ operationToHttpMethodMap.put(OperationEnum.CREATE, HttpMethod.POST);
+ operationToHttpMethodMap.put(OperationEnum.PATCH, HttpMethod.PATCH);
+ operationToHttpMethodMap.put(OperationEnum.UPDATE, HttpMethod.PUT);
+ operationToHttpMethodMap.put(OperationEnum.DELETE, HttpMethod.DELETE);
+ }
+
+ private final SdncProperties sdncProperties;
+ private final SdncRestconfClient sdncRestconfClient;
+ private final String topologyUrlData;
+ private final String topologyUrlOperational;
+
+ private Configuration jsonPathConfiguration = Configuration.builder()
+ .mappingProvider(new JacksonMappingProvider())
+ .jsonProvider(new JacksonJsonProvider())
+ .build();
+
+ /**
+ * Constructor for {@code SdncOperations}. This method also manipulates url properties.
+ *
+ * @param sdncProperties {@code SdncProperties}
+ * @param sdncRestconfClient {@code SdncRestconfClient}
+ */
+ public SdncOperations(final SdncProperties sdncProperties, final SdncRestconfClient sdncRestconfClient) {
+ this.sdncProperties = sdncProperties;
+ this.sdncRestconfClient = sdncRestconfClient;
+ topologyUrlOperational = getTopologyUrlOperational();
+ topologyUrlData = getTopologyUrlData();
+ }
+
+ /**
+ * This method fetches list of modules usind sdnc client.
+ *
+ * @param nodeId node id for node
+ * @return a collection of module schemas
+ */
+ public Collection<ModuleSchema> getModuleSchemasFromNode(final String nodeId) {
+ final String urlWithNodeId = prepareGetSchemaUrl(nodeId);
+ final ResponseEntity<String> modulesResponseEntity = sdncRestconfClient.getOperation(urlWithNodeId);
+ if (modulesResponseEntity.getStatusCode() == HttpStatus.OK) {
+ final String modulesResponseBody = modulesResponseEntity.getBody();
+ if (modulesResponseBody == null || modulesResponseBody.isBlank()) {
+ return Collections.emptyList();
+ }
+ return convertToModuleSchemas(modulesResponseBody);
+ }
+ throw new SdncException(
+ String.format("SDNC failed to get Modules Schema for node %s", nodeId),
+ (HttpStatus) modulesResponseEntity.getStatusCode(), modulesResponseEntity.getBody());
+ }
+
+ /**
+ * Get module schema.
+ *
+ * @param nodeId node ID
+ * @param moduleProperties module properties
+ * @return response entity
+ */
+ public ResponseEntity<String> getModuleResource(final String nodeId, final String moduleProperties) {
+ final String getYangResourceUrl = prepareGetOperationSchemaUrl(nodeId);
+ final HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.setContentType(MediaType.APPLICATION_JSON);
+ return sdncRestconfClient.httpOperationWithJsonData(
+ HttpMethod.POST, getYangResourceUrl, moduleProperties, httpHeaders);
+ }
+
+ /**
+ * This method fetches the resource data for given node identifier on given resource using sdnc client.
+ *
+ * @param nodeId network resource identifier
+ * @param resourceId resource identifier
+ * @param optionsParamInQuery fields query
+ * @param restConfContentQueryParam restConf content query param
+ * @return {@code ResponseEntity} response entity
+ */
+ public ResponseEntity<String> getResouceDataForOperationalAndRunning(final String nodeId,
+ final String resourceId,
+ final String optionsParamInQuery,
+ final String restConfContentQueryParam) {
+ final String getResourceDataUrl = prepareResourceDataUrl(nodeId,
+ resourceId, buildQueryParamMap(optionsParamInQuery, restConfContentQueryParam));
+ return sdncRestconfClient.getOperation(getResourceDataUrl);
+ }
+
+ /**
+ * Write resource data.
+ *
+ * @param nodeId network resource identifier
+ * @param resourceId resource identifier
+ * @param contentType http content type
+ * @param requestData request data
+ * @return {@code ResponseEntity} response entity
+ */
+ public ResponseEntity<String> writeData(final DataAccessRequest.OperationEnum operation,
+ final String nodeId,
+ final String resourceId,
+ final String contentType,
+ final String requestData) {
+ final String getResourceDataUrl = prepareWriteUrl(nodeId, resourceId);
+ final HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.setContentType(MediaType.parseMediaType(contentType));
+ final HttpMethod httpMethod = operationToHttpMethodMap.get(operation);
+ return sdncRestconfClient.httpOperationWithJsonData(httpMethod, getResourceDataUrl, requestData, httpHeaders);
+ }
+
+ private MultiValueMap<String, String> buildQueryParamMap(final String optionsParamInQuery,
+ final String restConfContentQueryParam) {
+ return getQueryParamsAsMap(optionsParamInQuery, restConfContentQueryParam);
+ }
+
+ private MultiValueMap<String, String> getQueryParamsAsMap(final String optionsParamInQuery,
+ final String restConfContentQueryParam) {
+ final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
+ if (optionsParamInQuery != null && !optionsParamInQuery.isBlank()) {
+ queryParams.setAll(extractQueryParams(optionsParamInQuery, restConfContentQueryParam));
+ }
+ return queryParams;
+ }
+
+ private String stripParenthesisFromOptionsQuery(final String optionsParamInQuery) {
+ return optionsParamInQuery.substring(1, optionsParamInQuery.length() - 1);
+ }
+
+ private String prepareGetSchemaUrl(final String nodeId) {
+ return addResource(addTopologyDataUrlwithNode(nodeId), GET_SCHEMA_URL);
+ }
+
+ private String prepareWriteUrl(final String nodeId, final String resourceId) {
+ return addResource(addTopologyDataUrlwithNode(nodeId), resourceId);
+ }
+
+ private String prepareGetOperationSchemaUrl(final String nodeId) {
+ return UriComponentsBuilder.fromUriString(topologyUrlOperational)
+ .pathSegment("node={nodeId}")
+ .pathSegment("yang-ext:mount")
+ .path(GET_SCHEMA_SOURCES_URL)
+ .buildAndExpand(nodeId).toUriString();
+ }
+
+ private String prepareResourceDataUrl(final String nodeId,
+ final String resourceId,
+ final MultiValueMap<String, String> queryMap) {
+ return addQuery(addResource(addTopologyDataUrlwithNode(nodeId), resourceId), queryMap);
+ }
+
+ private String addResource(final String url, final String resourceId) {
+ return UriComponentsBuilder.fromUriString(url)
+ .pathSegment(resourceId)
+ .buildAndExpand().toUriString();
+ }
+
+ private String addQuery(final String url, final MultiValueMap<String, String> queryMap) {
+ return UriComponentsBuilder.fromUriString(url)
+ .queryParams(queryMap)
+ .buildAndExpand().toUriString();
+ }
+
+ private String addTopologyDataUrlwithNode(final String nodeId) {
+ return UriComponentsBuilder.fromUriString(topologyUrlData)
+ .pathSegment("node={nodeId}")
+ .pathSegment("yang-ext:mount")
+ .buildAndExpand(nodeId).toUriString();
+ }
+
+ private List<ModuleSchema> convertToModuleSchemas(final String modulesListAsJson) {
+ try {
+ return JsonPath.using(jsonPathConfiguration).parse(modulesListAsJson).read(
+ PATH_TO_MODULE_SCHEMAS, new TypeRef<>() {
+ });
+ } catch (final JsonPathException jsonPathException) {
+ throw new SdncException("SDNC Response processing failed",
+ "SDNC response is not in the expected format.", jsonPathException);
+ }
+ }
+
+ private String getTopologyUrlData() {
+ return UriComponentsBuilder.fromUriString(TOPOLOGY_URL_TEMPLATE_DATA)
+ .path("topology={topologyId}")
+ .buildAndExpand(this.sdncProperties.getTopologyId()).toUriString();
+ }
+
+ private String getTopologyUrlOperational() {
+ return UriComponentsBuilder.fromUriString(
+ TOPOLOGY_URL_TEMPLATE_OPERATIONAL)
+ .path("topology={topologyId}")
+ .buildAndExpand(this.sdncProperties.getTopologyId()).toUriString();
+ }
+
+ private Map<String, String> extractQueryParams(final String optionsParamInQuery,
+ final String restConfContentQueryParam) {
+ final String QueryParamsAsString = stripParenthesisFromOptionsQuery(optionsParamInQuery)
+ + "," + restConfContentQueryParam;
+ final String[] splitTempQueryByComma = QueryParamsAsString.split(",");
+ return Arrays.stream(splitTempQueryByComma)
+ .map(queryParamPair -> queryParamPair.split("=", QUERY_PARAM_SPLIT_LIMIT))
+ .filter(queryParam -> queryParam.length > 1)
+ .collect(Collectors.toMap(
+ queryParam -> queryParam[QUERY_PARAM_NAME_INDEX],
+ queryParam -> queryParam[QUERY_PARAM_VALUE_INDEX]));
+ }
+}