Add DME integration 03/11803/1
authoraravind.est <aravindhan.a@est.tech>
Wed, 20 Sep 2023 11:22:18 +0000 (12:22 +0100)
committeraravind.est <aravindhan.a@est.tech>
Wed, 20 Sep 2023 16:20:31 +0000 (17:20 +0100)
Integrated DME to create info type, producer and consumer as per the configuration from rApp package.

Issue-ID: NONRTRIC-913
Signed-off-by: aravind.est <aravindhan.a@est.tech>
Change-Id: Ib7a41f8a4200503f3a0b0ffee8e80a0cda3c0871

40 files changed:
pom.xml
rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/service/AcmDeployer.java
rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/AcmDeployerTest.java
rapp-manager-acm/src/test/resources/valid-rapp-package.csar
rapp-manager-application/pom.xml
rapp-manager-application/src/main/java/com/oransc/rappmanager/BeanConfiguration.java
rapp-manager-application/src/main/java/com/oransc/rappmanager/service/RappService.java
rapp-manager-application/src/main/resources/application.yaml
rapp-manager-application/src/main/resources/resource-csar/Files/Acm/definition/compositions.json
rapp-manager-application/src/main/resources/resource-csar/Files/Acm/instances/k8s-instance.json
rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/json-file-consumer.json [new file with mode: 0755]
rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/xml-file-consumer.json [new file with mode: 0755]
rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/json-file-data-producer.json [new file with mode: 0755]
rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/xml-file-data-producer.json [new file with mode: 0755]
rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/json-file-data-from-filestore.json [new file with mode: 0755]
rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/xml-file-data-from-filestore.json [new file with mode: 0755]
rapp-manager-application/src/main/resources/resource-csar/asd.mf
rapp-manager-application/src/test/java/com/oransc/rappmanager/rest/RappInstanceControllerTest.java
rapp-manager-application/src/test/java/com/oransc/rappmanager/service/RappServiceTest.java
rapp-manager-dme/pom.xml [new file with mode: 0755]
rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/configuration/DmeConfiguration.java [new file with mode: 0755]
rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/service/DmeDeployer.java [new file with mode: 0755]
rapp-manager-dme/src/main/resources/openapi/ics-api.yaml [new file with mode: 0755]
rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/BeanTestConfiguration.java [new file with mode: 0755]
rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/DmeDeployerTest.java [new file with mode: 0755]
rapp-manager-dme/src/test/java/com/oransc/rappmanager/models/rapp/RappDmeResourceBuilder.java [new file with mode: 0755]
rapp-manager-dme/src/test/resources/application.yaml [new file with mode: 0755]
rapp-manager-dme/src/test/resources/valid-rapp-package-new-info-type.csar [new file with mode: 0755]
rapp-manager-dme/src/test/resources/valid-rapp-package.csar [new file with mode: 0755]
rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandler.java
rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/Rapp.java
rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappEvent.java
rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappResources.java
rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappDMEInstance.java [new file with mode: 0755]
rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappInstance.java
rapp-manager-models/src/main/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfig.java
rapp-manager-models/src/test/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandlerTest.java
rapp-manager-models/src/test/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfigTest.java
rapp-manager-models/src/test/resources/valid-rapp-package.csar
rapp-manager-sme/src/main/java/com/oransc/rappmanager/sme/service/SmeDeployer.java

diff --git a/pom.xml b/pom.xml
index a0f222a..9491a02 100755 (executable)
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,7 @@
         <module>rapp-manager-models</module>
         <module>rapp-manager-acm</module>
         <module>rapp-manager-sme</module>
+        <module>rapp-manager-dme</module>
         <module>rapp-manager-application</module>
     </modules>
     <repositories>
index 41f4bae..eb93ed3 100755 (executable)
@@ -25,7 +25,6 @@ import com.oransc.rappmanager.models.RappDeployer;
 import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler;
 import com.oransc.rappmanager.models.rapp.Rapp;
 import com.oransc.rappmanager.models.rapp.RappEvent;
-import com.oransc.rappmanager.models.rapp.RappState;
 import com.oransc.rappmanager.models.rappinstance.RappACMInstance;
 import com.oransc.rappmanager.models.rappinstance.RappInstance;
 import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine;
@@ -172,7 +171,6 @@ public class AcmDeployer implements RappDeployer {
                 rapp.setCompositionId(commissioningResponse.getCompositionId());
                 logger.info("Priming automation Composition");
                 primeACMComposition(commissioningResponse.getCompositionId(), PrimeOrder.PRIME);
-                rapp.setState(RappState.PRIMED);
                 return true;
             } else {
                 logger.error("Failed to create automation composition");
@@ -189,7 +187,6 @@ public class AcmDeployer implements RappDeployer {
             primeACMComposition(rapp.getCompositionId(), PrimeOrder.DEPRIME);
             CommissioningResponse commissioningResponse = deleteComposition(rapp.getCompositionId());
             if (commissioningResponse != null) {
-                rapp.setState(RappState.COMMISSIONED);
                 return true;
             }
         } catch (Exception e) {
index 59e69a6..f3016b0 100755 (executable)
@@ -287,8 +287,6 @@ class AcmDeployerTest {
         boolean primeRapp = acmDeployer.primeRapp(rapp);
         mockServer.verify();
         assertTrue(primeRapp);
-        assertEquals(RappState.PRIMED, rapp.getState());
-
     }
 
     @Test
index b8b36e3..398a516 100755 (executable)
Binary files a/rapp-manager-acm/src/test/resources/valid-rapp-package.csar and b/rapp-manager-acm/src/test/resources/valid-rapp-package.csar differ
index ee830cf..5bd7dd1 100755 (executable)
             <artifactId>rapp-manager-sme</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.o-ran-sc.nonrtric.plt.rappmanager</groupId>
+            <artifactId>rapp-manager-dme</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.onap.policy.clamp</groupId>
             <artifactId>policy-clamp-models</artifactId>
index 0d3bd3d..4e7f7fd 100755 (executable)
@@ -23,6 +23,9 @@ import com.oransc.rappmanager.acm.configuration.ACMConfiguration;
 import com.oransc.rappmanager.acm.rest.AutomationCompositionDefinitionApiClient;
 import com.oransc.rappmanager.acm.rest.AutomationCompositionInstanceApiClient;
 import com.oransc.rappmanager.acm.rest.ParticipantMonitoringApiClient;
+import com.oransc.rappmanager.dme.configuration.DmeConfiguration;
+import com.oransc.rappmanager.dme.rest.DataConsumerApiClient;
+import com.oransc.rappmanager.dme.rest.DataProducerRegistrationApiClient;
 import com.oransc.rappmanager.sme.configuration.SmeConfiguration;
 import com.oransc.rappmanager.sme.provider.rest.DefaultApiClient;
 import lombok.RequiredArgsConstructor;
@@ -40,6 +43,7 @@ public class BeanConfiguration {
 
     private final ACMConfiguration acmConfiguration;
     private final SmeConfiguration smeConfiguration;
+    private final DmeConfiguration dmeConfiguration;
 
     @Bean
     public RestTemplate restTemplate(RestTemplateBuilder builder) {
@@ -108,6 +112,23 @@ public class BeanConfiguration {
         return new com.oransc.rappmanager.sme.invoker.rest.DefaultApiClient(apiClient);
     }
 
+    @Bean
+    public com.oransc.rappmanager.dme.ApiClient dmeApiClient(RestTemplate restTemplate) {
+        com.oransc.rappmanager.dme.ApiClient apiClient = new com.oransc.rappmanager.dme.ApiClient(restTemplate);
+        return apiClient.setBasePath(dmeConfiguration.getBaseUrl());
+    }
+
+    @Bean
+    public DataProducerRegistrationApiClient dataProducerRegistrationApiClient(
+            com.oransc.rappmanager.dme.ApiClient apiClient) {
+        return new DataProducerRegistrationApiClient(apiClient);
+    }
+
+    @Bean
+    public DataConsumerApiClient dataConsumerApiClient(com.oransc.rappmanager.dme.ApiClient apiClient) {
+        return new DataConsumerApiClient(apiClient);
+    }
+
     @Bean
     public CacheManager cacheManager() {
         return new ConcurrentMapCacheManager();
index 6411b2b..1b9fb68 100755 (executable)
@@ -19,6 +19,7 @@
 package com.oransc.rappmanager.service;
 
 import com.oransc.rappmanager.acm.service.AcmDeployer;
+import com.oransc.rappmanager.dme.service.DmeDeployer;
 import com.oransc.rappmanager.models.rapp.Rapp;
 import com.oransc.rappmanager.models.rapp.RappEvent;
 import com.oransc.rappmanager.models.rapp.RappState;
@@ -37,13 +38,16 @@ public class RappService {
 
     private final AcmDeployer acmDeployer;
     private final SmeDeployer smeDeployer;
+    private final DmeDeployer dmeDeployer;
     private final RappInstanceStateMachine rappInstanceStateMachine;
     private static final String STATE_TRANSITION_NOT_PERMITTED = "State transition from %s to %s is not permitted.";
 
     public ResponseEntity<String> primeRapp(Rapp rapp) {
         if (rapp.getState().equals(RappState.COMMISSIONED)) {
             rapp.setState(RappState.PRIMING);
-            if (!acmDeployer.primeRapp(rapp)) {
+            if (acmDeployer.primeRapp(rapp) && dmeDeployer.primeRapp(rapp)) {
+                rapp.setState(RappState.PRIMED);
+            } else {
                 rapp.setState(RappState.COMMISSIONED);
             }
             return ResponseEntity.ok().build();
@@ -57,7 +61,9 @@ public class RappService {
     public ResponseEntity<String> deprimeRapp(Rapp rapp) {
         if (rapp.getState().equals(RappState.PRIMED) && rapp.getRappInstances().isEmpty()) {
             rapp.setState(RappState.DEPRIMING);
-            if (!acmDeployer.deprimeRapp(rapp)) {
+            if (acmDeployer.deprimeRapp(rapp) && dmeDeployer.deprimeRapp(rapp)) {
+                rapp.setState(RappState.COMMISSIONED);
+            } else {
                 rapp.setState(RappState.PRIMED);
             }
             return ResponseEntity.ok().build();
@@ -75,8 +81,8 @@ public class RappService {
     public ResponseEntity<String> deployRappInstance(Rapp rapp, RappInstance rappInstance) {
         if (rappInstance.getState().equals(RappInstanceState.UNDEPLOYED)) {
             rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DEPLOYING);
-            if (acmDeployer.deployRappInstance(rapp, rappInstance) && smeDeployer.deployRappInstance(rapp,
-                    rappInstance)) {
+            if (acmDeployer.deployRappInstance(rapp, rappInstance) && smeDeployer.deployRappInstance(rapp, rappInstance)
+                        && dmeDeployer.deployRappInstance(rapp, rappInstance)) {
                 return ResponseEntity.accepted().build();
             }
             return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
@@ -91,7 +97,7 @@ public class RappService {
         if (rappInstance.getState().equals(RappInstanceState.DEPLOYED)) {
             rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.UNDEPLOYING);
             if (acmDeployer.undeployRappInstance(rapp, rappInstance) && smeDeployer.undeployRappInstance(rapp,
-                    rappInstance)) {
+                    rappInstance) && dmeDeployer.undeployRappInstance(rapp, rappInstance)) {
                 return ResponseEntity.accepted().build();
             }
             return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
index 0672fd2..8a30abb 100755 (executable)
@@ -1,23 +1,25 @@
 rappmanager:
   csarlocation: src/test/resources/csar
   acm:
-    baseurl: http://10.101.3.22:30442/onap/policy/clamp/acm/v2/
+    baseurl: http://10.101.2.41:30442/onap/policy/clamp/acm/v2/
     username: runtimeUser
     password: zb!XztG34
-    maxRetries: 3
+    maxRetries: 10
     retryInterval: 2 #seconds
   sme:
-    baseurl: http://localhost:60821 #http://10.101.3.22:61761
+    baseurl: http://localhost:56571 #http://10.101.3.22:61761
     providerBasePath: /api-provider-management/v1/
     invokerBasePath: /api-invoker-management/v1/
     publishApiBasePath: /published-apis/v1/
     maxRetries: 3
     retryInterval: 2 #seconds
+  dme:
+    baseurl: http://localhost:63475 #http://10.101.3.22:61761
 
 logging:
   level:
     root: INFO
-    com.oransc.rapps: DEBUG
+    com.oransc: DEBUG
     org.apache.http: DEBUG
     httpclient.wire: DEBUG
     org.springframework.web.client.RestTemplate: TRACE
index bc568ad..e64d882 100755 (executable)
             "name": "org.onap.policy.clamp.acm.KserveParticipant",
             "version": "2.3.4"
           },
-          "uninitializedToPassiveTimeout": 60,
+          "uninitializedToPassiveTimeout": 300,
           "statusCheckInterval": 30
         }
       },
index 286a943..562b5a2 100755 (executable)
@@ -22,7 +22,7 @@
           "podName": "ransliceassurance",
           "repository": {
             "repoName": "local",
-            "address": "http://10.101.3.22:8879/charts"
+            "address": "http://10.101.2.41:8879/charts"
           }
         }
       }
diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/json-file-consumer.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/json-file-consumer.json
new file mode 100755 (executable)
index 0000000..b18a643
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "info_type_id": "json-file-data-from-filestore",
+  "job_owner": "console",
+  "status_notification_uri": "http://callback.nonrtric:80/post",
+  "job_definition": {
+    "db-url": "http://influxdb2.nonrtric:8086",
+    "db-org": "est",
+    "db-bucket": "pm-bucket",
+    "db-token": "token",
+    "filterType": "pmdata",
+    "filter": {}
+  }
+}
\ No newline at end of file
diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/xml-file-consumer.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/xml-file-consumer.json
new file mode 100755 (executable)
index 0000000..b33482a
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "info_type_id": "xml-file-data-from-filestore",
+  "job_owner": "console",
+  "status_notification_uri": "http://callback.nonrtric:80/post",
+  "job_definition": {
+    "db-url": "http://influxdb2.nonrtric:8086",
+    "db-org": "est",
+    "db-bucket": "pm-bucket",
+    "db-token": "token",
+    "filterType": "pmdata",
+    "filter": {}
+  }
+}
\ No newline at end of file
diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/json-file-data-producer.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/json-file-data-producer.json
new file mode 100755 (executable)
index 0000000..ed74384
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "info_job_callback_url": "http://localhost/jsonproducerjobcallback",
+  "info_producer_supervision_callback_url": "http://localhost/jsonproducersupervisioncallback",
+  "supported_info_types": [
+    "json-file-data-from-filestore"
+  ]
+}
\ No newline at end of file
diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/xml-file-data-producer.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/xml-file-data-producer.json
new file mode 100755 (executable)
index 0000000..33075de
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "info_job_callback_url": "http://localhost/xmlproducerjobcallback",
+  "info_producer_supervision_callback_url": "http://localhost/xmlproducersupervisioncallback",
+  "supported_info_types": [
+    "xml-file-data-from-filestore"
+  ]
+}
\ No newline at end of file
diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/json-file-data-from-filestore.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/json-file-data-from-filestore.json
new file mode 100755 (executable)
index 0000000..a5f9d02
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "info_job_data_schema": {
+    "schema": "http://json-schema.org/draft-07/schema#",
+    "title": "json-file-data-from-filestore",
+    "description": "json-file-data-from-filestore",
+    "type": "object"
+  }
+}
\ No newline at end of file
diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/xml-file-data-from-filestore.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/xml-file-data-from-filestore.json
new file mode 100755 (executable)
index 0000000..3585603
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "info_job_data_schema": {
+    "schema": "http://json-schema.org/draft-07/schema#",
+    "title": "xml-file-data-to-filestore",
+    "description": "xml-file-data-to-filestore",
+    "type": "object"
+  }
+}
\ No newline at end of file
index 39218a5..09818fc 100755 (executable)
@@ -17,7 +17,24 @@ Source: TOSCA-Metadata/TOSCA.meta
 Source: Artifacts/Deployment/HELM/free5gc-1.1.3.tgz\r
 Source: Artifacts/Deployment/HELM/ueransim-2.0.14.tgz\r
 Source: Files/rapp1/rapp.zip\r
-Source: Files/Acm/instantiation.yaml\r
+Source: Files/Acm/definition/compositions.json\r
+Source: Files/Acm/instances/a1pms-instance.json\r
+Source: Files/Acm/instances/k8s-instance.json\r
+Source: Files/Acm/instances/kserve-instance.json\r
+Source: Files/Dme/infoconsumers/json-file-consumer.json\r
+Source: Files/Dme/infoconsumers/xml-file-consumer.json\r
+Source: Files/Dme/infoproducers/json-file-data-producer.json\r
+Source: Files/Dme/infoproducers/xml-file-data-producer.json\r
+Source: Files/Dme/infotypes/json-file-data-from-filestore.json\r
+Source: Files/Dme/infotypes/xml-file-data-from-filestore.json\r
+Source: Files/Sme/invokers/invoker-app1.json\r
+Source: Files/Sme/invokers/invoker-app2.json\r
+Source: Files/Sme/provider/aef-provider-function.json\r
+Source: Files/Sme/provider/amf-provider-function.json\r
+Source: Files/Sme/provider/apf-provider-function.json\r
+Source: Files/Sme/provider/gateway-provider-function.json\r
+Source: Files/Sme/serviceapis/api-set-1.json\r
+Source: Files/Sme/serviceapis/api-set-2.json\r
 \r
 non_mano_artifact_sets:\r
     onap_ves_events:\r
index d6ba4ce..9eb959a 100755 (executable)
@@ -8,6 +8,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.oransc.rappmanager.acm.service.AcmDeployer;
+import com.oransc.rappmanager.dme.service.DmeDeployer;
 import com.oransc.rappmanager.models.rappinstance.DeployOrder;
 import com.oransc.rappmanager.models.rapp.Rapp;
 import com.oransc.rappmanager.models.rappinstance.RappInstance;
@@ -49,6 +50,9 @@ class RappInstanceControllerTest {
     @MockBean
     SmeDeployer smeDeployer;
 
+    @MockBean
+    DmeDeployer dmeDeployer;
+
     @MockBean
     SmeLifecycleManager smeLifecycleManager;
 
@@ -146,6 +150,7 @@ class RappInstanceControllerTest {
         rappInstanceDeployOrder.setDeployOrder(DeployOrder.DEPLOY);
         when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true);
         mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId)
                                 .contentType(MediaType.APPLICATION_JSON)
                                 .content(objectMapper.writeValueAsString(rappInstanceDeployOrder)))
@@ -164,6 +169,7 @@ class RappInstanceControllerTest {
         rappInstanceDeployOrder.setDeployOrder(DeployOrder.DEPLOY);
         when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true);
         mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId)
                                 .contentType(MediaType.APPLICATION_JSON)
                                 .content(objectMapper.writeValueAsString(rappInstanceDeployOrder)))
@@ -176,6 +182,7 @@ class RappInstanceControllerTest {
         rappInstanceDeployOrder.setDeployOrder(DeployOrder.DEPLOY);
         when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true);
         mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", UUID.randomUUID(),
                                 UUID.randomUUID()).contentType(MediaType.APPLICATION_JSON)
                                 .content(objectMapper.writeValueAsString(rappInstanceDeployOrder)))
@@ -194,6 +201,7 @@ class RappInstanceControllerTest {
         rappInstanceDeployOrder.setDeployOrder(DeployOrder.UNDEPLOY);
         when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId)
                                 .contentType(MediaType.APPLICATION_JSON)
                                 .content(objectMapper.writeValueAsString(rappInstanceDeployOrder)))
@@ -212,6 +220,7 @@ class RappInstanceControllerTest {
         rappInstanceDeployOrder.setDeployOrder(DeployOrder.UNDEPLOY);
         when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId)
                                 .contentType(MediaType.APPLICATION_JSON)
                                 .content(objectMapper.writeValueAsString(rappInstanceDeployOrder)))
@@ -226,6 +235,7 @@ class RappInstanceControllerTest {
         rappInstanceDeployOrder.setDeployOrder(DeployOrder.UNDEPLOY);
         when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId)
                                 .contentType(MediaType.APPLICATION_JSON)
                                 .content(objectMapper.writeValueAsString(rappInstanceDeployOrder)))
index dbb3da7..c50f0b4 100755 (executable)
@@ -5,6 +5,7 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.when;
 
 import com.oransc.rappmanager.acm.service.AcmDeployer;
+import com.oransc.rappmanager.dme.service.DmeDeployer;
 import com.oransc.rappmanager.models.rapp.Rapp;
 import com.oransc.rappmanager.models.rapp.RappState;
 import com.oransc.rappmanager.models.rappinstance.RappInstance;
@@ -34,6 +35,9 @@ class RappServiceTest {
     @MockBean
     SmeDeployer smeDeployer;
 
+    @MockBean
+    DmeDeployer dmeDeployer;
+
     @MockBean
     SmeLifecycleManager smeLifecycleManager;
 
@@ -44,15 +48,15 @@ class RappServiceTest {
 
     private final String validRappFile = "valid-rapp-package.csar";
 
-    private final String invalidRappFile = "invalid-rapp-package.csar";
-
 
     @Test
     void testPrimeRapp() {
         Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
                             .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
         when(acmDeployer.primeRapp(any())).thenReturn(true);
+        when(dmeDeployer.primeRapp(any())).thenReturn(true);
         assertEquals(HttpStatus.OK, rappService.primeRapp(rapp).getStatusCode());
+        assertEquals(RappState.PRIMED, rapp.getState());
     }
 
     @Test
@@ -67,23 +71,49 @@ class RappServiceTest {
         Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
                             .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
         when(acmDeployer.primeRapp(any())).thenReturn(false);
+        when(dmeDeployer.primeRapp(any())).thenReturn(true);
+        assertEquals(HttpStatus.OK, rappService.primeRapp(rapp).getStatusCode());
+        assertEquals(RappState.COMMISSIONED, rapp.getState());
+    }
+    @Test
+    void testPrimeRappDmeFailure() {
+        Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
+                            .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
+        when(acmDeployer.primeRapp(any())).thenReturn(true);
+        when(dmeDeployer.primeRapp(any())).thenReturn(false);
         assertEquals(HttpStatus.OK, rappService.primeRapp(rapp).getStatusCode());
+        assertEquals(RappState.COMMISSIONED, rapp.getState());
     }
 
+
     @Test
     void testDeprimeRapp() {
         Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
                             .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build();
         when(acmDeployer.deprimeRapp(any())).thenReturn(true);
+        when(dmeDeployer.deprimeRapp(any())).thenReturn(true);
         assertEquals(HttpStatus.OK, rappService.deprimeRapp(rapp).getStatusCode());
+        assertEquals(RappState.COMMISSIONED, rapp.getState());
     }
 
     @Test
-    void testDeprimeRappFailure() {
+    void testDeprimeRappAcmFailure() {
         Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
                             .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build();
         when(acmDeployer.deprimeRapp(any())).thenReturn(false);
+        when(dmeDeployer.deprimeRapp(any())).thenReturn(true);
         assertEquals(HttpStatus.OK, rappService.deprimeRapp(rapp).getStatusCode());
+        assertEquals(RappState.PRIMED, rapp.getState());
+    }
+
+    @Test
+    void testDeprimeRappDmeFailure() {
+        Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
+                            .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build();
+        when(acmDeployer.deprimeRapp(any())).thenReturn(true);
+        when(dmeDeployer.deprimeRapp(any())).thenReturn(false);
+        assertEquals(HttpStatus.OK, rappService.deprimeRapp(rapp).getStatusCode());
+        assertEquals(RappState.PRIMED, rapp.getState());
     }
 
     @Test
@@ -91,14 +121,16 @@ class RappServiceTest {
         Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
                             .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
         assertEquals(HttpStatus.BAD_REQUEST, rappService.deprimeRapp(rapp).getStatusCode());
+        assertEquals(RappState.COMMISSIONED, rapp.getState());
     }
 
     @Test
     void testDeprimeRappActiveInstances() {
         Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
-                            .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
+                            .packageLocation(validCsarFileLocation).state(RappState.PRIMED)
                             .rappInstances(Map.of(UUID.randomUUID(), new RappInstance())).build();
         assertEquals(HttpStatus.BAD_REQUEST, rappService.deprimeRapp(rapp).getStatusCode());
+        assertEquals(RappState.PRIMED, rapp.getState());
     }
 
     @Test
@@ -109,6 +141,7 @@ class RappServiceTest {
         rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
         when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true);
         assertEquals(HttpStatus.ACCEPTED, rappService.deployRappInstance(rapp, rappInstance).getStatusCode());
     }
 
@@ -120,6 +153,19 @@ class RappServiceTest {
         rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
         when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.deployRappInstance(any(), any())).thenReturn(false);
+        when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true);
+        assertEquals(HttpStatus.BAD_GATEWAY, rappService.deployRappInstance(rapp, rappInstance).getStatusCode());
+    }
+
+    @Test
+    void testDeployRappInstanceDmeFailure() {
+        Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
+                            .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build();
+        RappInstance rappInstance = new RappInstance();
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true);
+        when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(false);
         assertEquals(HttpStatus.BAD_GATEWAY, rappService.deployRappInstance(rapp, rappInstance).getStatusCode());
     }
 
@@ -132,6 +178,7 @@ class RappServiceTest {
         rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
         when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         assertEquals(HttpStatus.ACCEPTED, rappService.undeployRappInstance(rapp, rappInstance).getStatusCode());
     }
 
@@ -144,6 +191,20 @@ class RappServiceTest {
         rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
         when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(false);
+        when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
+        assertEquals(HttpStatus.BAD_GATEWAY, rappService.undeployRappInstance(rapp, rappInstance).getStatusCode());
+    }
+
+    @Test
+    void testUndeployRappInstanceDmeFailure() {
+        Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
+                            .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build();
+        RappInstance rappInstance = new RappInstance();
+        rappInstance.setState(RappInstanceState.DEPLOYED);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true);
+        when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
+        when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(false);
         assertEquals(HttpStatus.BAD_GATEWAY, rappService.undeployRappInstance(rapp, rappInstance).getStatusCode());
     }
 
@@ -156,6 +217,7 @@ class RappServiceTest {
         rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
         when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(false);
+        when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true);
         assertEquals(HttpStatus.BAD_REQUEST, rappService.undeployRappInstance(rapp, rappInstance).getStatusCode());
     }
 }
diff --git a/rapp-manager-dme/pom.xml b/rapp-manager-dme/pom.xml
new file mode 100755 (executable)
index 0000000..6b8e83f
--- /dev/null
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* ========================LICENSE_START=================================
+* O-RAN-SC
+* %%
+* 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.
+* ========================LICENSE_END===================================
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.o-ran-sc.nonrtric.plt</groupId>
+        <artifactId>rappmanager</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.o-ran-sc.nonrtric.plt.rappmanager</groupId>
+    <artifactId>rapp-manager-dme</artifactId>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.o-ran-sc.nonrtric.plt.rappmanager</groupId>
+            <artifactId>rapp-manager-models</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openapitools</groupId>
+            <artifactId>jackson-databind-nullable</artifactId>
+            <version>${openapi.jackson.databind.nullable.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.openapitools</groupId>
+                <artifactId>openapi-generator-maven-plugin</artifactId>
+                <version>${openapi.maven.version}</version>
+                <executions>
+                    <execution>
+                        <id>dme-spec-generator</id>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <inputSpec>${project.basedir}/src/main/resources/openapi/ics-api.yaml</inputSpec>
+                            <generatorName>java</generatorName>
+                            <library>resttemplate</library>
+                            <generateApiTests>false</generateApiTests>
+                            <generateModelTests>false</generateModelTests>
+                            <generateApiDocumentation>false</generateApiDocumentation>
+                            <generateModelDocumentation>false</generateModelDocumentation>
+                            <generateModels>true</generateModels>
+                            <additionalProperties>
+                                <additionalProperty>apiNameSuffix=ApiClient</additionalProperty>
+                            </additionalProperties>
+                            <configOptions>
+                                <sourceFolder>src/main/java</sourceFolder>
+                                <useJakartaEe>true</useJakartaEe>
+                                <invokerPackage>com.oransc.rappmanager.dme</invokerPackage>
+                                <apiPackage>com.oransc.rappmanager.dme.rest</apiPackage>
+                                <modelPackage>com.oransc.rappmanager.dme.data</modelPackage>
+                                <generateClientAsBean>false</generateClientAsBean>
+                            </configOptions>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/configuration/DmeConfiguration.java b/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/configuration/DmeConfiguration.java
new file mode 100755 (executable)
index 0000000..e7cc151
--- /dev/null
@@ -0,0 +1,30 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2023 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package com.oransc.rappmanager.dme.configuration;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConfigurationProperties(prefix = "rappmanager.dme")
+@Data
+public class DmeConfiguration {
+    String baseUrl;
+}
diff --git a/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/service/DmeDeployer.java b/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/service/DmeDeployer.java
new file mode 100755 (executable)
index 0000000..388a565
--- /dev/null
@@ -0,0 +1,230 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2023 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package com.oransc.rappmanager.dme.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.oransc.rappmanager.dme.data.ConsumerJob;
+import com.oransc.rappmanager.dme.data.ProducerInfoTypeInfo;
+import com.oransc.rappmanager.dme.data.ProducerRegistrationInfo;
+import com.oransc.rappmanager.dme.rest.DataConsumerApiClient;
+import com.oransc.rappmanager.dme.rest.DataProducerRegistrationApiClient;
+import com.oransc.rappmanager.models.RappDeployer;
+import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler;
+import com.oransc.rappmanager.models.rapp.Rapp;
+import com.oransc.rappmanager.models.rapp.RappEvent;
+import com.oransc.rappmanager.models.rappinstance.RappInstance;
+import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DmeDeployer implements RappDeployer {
+
+    Logger logger = LoggerFactory.getLogger(DmeDeployer.class);
+
+    private final DataProducerRegistrationApiClient dataProducerRegistrationApiClient;
+
+    private final DataConsumerApiClient dataConsumerApiClient;
+
+    private final RappCsarConfigurationHandler rappCsarConfigurationHandler;
+
+    private final ObjectMapper objectMapper;
+
+    private final RappInstanceStateMachine rappInstanceStateMachine;
+
+    @Override
+    public boolean deployRappInstance(Rapp rapp, RappInstance rappInstance) {
+        logger.debug("Deploying DME functions for RappInstance {}", rappInstance.getRappInstanceId());
+        boolean deployState = true;
+        if (rappInstance.getDme().getInfoTypesProducer() != null
+                    || rappInstance.getDme().getInfoTypeConsumer() != null) {
+            Set<String> infoTypes = new HashSet<>();
+            if (rappInstance.getDme().getInfoTypesProducer() != null) {
+                infoTypes.addAll(rappInstance.getDme().getInfoTypesProducer());
+            }
+            if (rappInstance.getDme().getInfoTypeConsumer() != null) {
+                infoTypes.add(rappInstance.getDme().getInfoTypeConsumer());
+            }
+            deployState = createInfoTypes(rapp, infoTypes);
+        }
+        if (rappInstance.getDme().getInfoProducer() != null) {
+            deployState = deployState && createInfoProducer(rapp, rappInstance.getDme().getInfoProducer());
+        }
+        if (rappInstance.getDme().getInfoConsumer() != null) {
+            deployState = deployState && createInfoConsumer(rapp, rappInstance.getDme().getInfoConsumer());
+        }
+        if (deployState) {
+            rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DMEDEPLOYED);
+        } else {
+            rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DMEDEPLOYFAILED);
+        }
+        return deployState;
+    }
+
+    @Override
+    public boolean undeployRappInstance(Rapp rapp, RappInstance rappInstance) {
+        logger.debug("Undeploying DME functions for RappInstance {}", rappInstance.getRappInstanceId());
+        boolean undeployState = true;
+        if (rappInstance.getDme().getInfoConsumer() != null) {
+            undeployState = deleteInfoConsumer(rapp, rappInstance.getDme().getInfoConsumer());
+        }
+        if (rappInstance.getDme().getInfoProducer() != null) {
+            undeployState = undeployState && deleteInfoProducer(rapp, rappInstance.getDme().getInfoProducer());
+        }
+        if (undeployState) {
+            rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DMEUNDEPLOYED);
+        } else {
+            rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DMEUNDEPLOYFAILED);
+        }
+        return undeployState;
+    }
+
+    @Override
+    public boolean primeRapp(Rapp rapp) {
+        logger.debug("Priming DME functions for rApp {}", rapp.getRappId());
+        try {
+            Set<String> requiredInfoTypes = new HashSet<>();
+            for (String producerResourceName : rapp.getRappResources().getDme().getInfoProducers()) {
+                String producerPayload =
+                        rappCsarConfigurationHandler.getDmeInfoProducerPayload(rapp, producerResourceName);
+                ProducerRegistrationInfo producerRegistrationInfo =
+                        objectMapper.readValue(producerPayload, ProducerRegistrationInfo.class);
+                requiredInfoTypes.addAll(producerRegistrationInfo.getSupportedInfoTypes());
+            }
+            for (String consumerResourceName : rapp.getRappResources().getDme().getInfoConsumers()) {
+                String consumerPayload =
+                        rappCsarConfigurationHandler.getDmeInfoConsumerPayload(rapp, consumerResourceName);
+                ConsumerJob consumerJob = objectMapper.readValue(consumerPayload, ConsumerJob.class);
+                requiredInfoTypes.add(consumerJob.getInfoTypeId());
+            }
+            Set<String> allInfoTypes = new HashSet<>(rapp.getRappResources().getDme().getInfoTypes());
+            requiredInfoTypes.removeAll(allInfoTypes);
+            if (!requiredInfoTypes.isEmpty()) {
+                allInfoTypes.addAll(dataProducerRegistrationApiClient.getInfoTypdentifiers());
+                requiredInfoTypes.removeAll(allInfoTypes);
+                if (!requiredInfoTypes.isEmpty()) {
+                    logger.info("Invalid rapp package as the following info types cannot be found {}",
+                            requiredInfoTypes);
+                    rapp.setIsDmeValid(false);
+                } else {
+                    rapp.setIsDmeValid(true);
+                }
+            } else {
+                rapp.setIsDmeValid(true);
+            }
+            return true;
+        } catch (Exception e) {
+            logger.warn("Failed to prime DME", e);
+            rapp.setIsDmeValid(false);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean deprimeRapp(Rapp rapp) {
+        logger.debug("Depriming DME functions for rApp {}", rapp.getRappId());
+        rapp.setIsDmeValid(null);
+        return true;
+    }
+
+    boolean createInfoTypes(Rapp rApp, Set<String> infoTypes) {
+        logger.debug("Creating DME info types {} for rApp {}", infoTypes, rApp.getRappId());
+        try {
+            Map<String, ProducerInfoTypeInfo> producerInfoTypeInfoMap = new HashMap<>();
+            for (String infoType : infoTypes) {
+                String infoTypePayload = rappCsarConfigurationHandler.getDmeInfoTypePayload(rApp, infoType);
+                if (infoTypePayload != null && !infoTypePayload.isEmpty()) {
+                    producerInfoTypeInfoMap.put(infoType,
+                            objectMapper.readValue(infoTypePayload, ProducerInfoTypeInfo.class));
+                }
+            }
+            return producerInfoTypeInfoMap.entrySet().stream().map(stringProducerInfoTypeInfoEntry -> {
+                ResponseEntity<Object> objectResponseEntity = dataProducerRegistrationApiClient.putInfoTypeWithHttpInfo(
+                        stringProducerInfoTypeInfoEntry.getKey(), stringProducerInfoTypeInfoEntry.getValue());
+                return objectResponseEntity.getStatusCode().is2xxSuccessful();
+            }).reduce(true, (a, b) -> a && b);
+        } catch (Exception e) {
+            logger.warn("Error in creating info types {} for rApp {}", infoTypes, rApp.getRappId(), e);
+            return false;
+        }
+    }
+
+    boolean createInfoProducer(Rapp rApp, String producerResource) {
+        logger.debug("Creating DME info producer {} for rApp {}", producerResource, rApp.getRappId());
+        try {
+            String infoProducerPayload = rappCsarConfigurationHandler.getDmeInfoProducerPayload(rApp, producerResource);
+            ProducerRegistrationInfo producerRegistrationInfo =
+                    objectMapper.readValue(infoProducerPayload, ProducerRegistrationInfo.class);
+
+            ResponseEntity<Object> objectResponseEntity =
+                    dataProducerRegistrationApiClient.putInfoProducerWithHttpInfo(producerResource,
+                            producerRegistrationInfo);
+            return objectResponseEntity.getStatusCode().is2xxSuccessful();
+        } catch (Exception e) {
+            logger.warn("Error in creating info producer {} for rApp {}", producerResource, rApp.getRappId(), e);
+            return false;
+        }
+    }
+
+    boolean createInfoConsumer(Rapp rApp, String consumerResource) {
+        logger.debug("Creating DME info consumer {} for rApp {}", consumerResource, rApp.getRappId());
+        try {
+            String infoJobPayload = rappCsarConfigurationHandler.getDmeInfoConsumerPayload(rApp, consumerResource);
+            ConsumerJob consumerJob = objectMapper.readValue(infoJobPayload, ConsumerJob.class);
+            ResponseEntity<Object> objectResponseEntity =
+                    dataConsumerApiClient.putIndividualInfoJobWithHttpInfo(consumerResource, consumerJob);
+            return objectResponseEntity.getStatusCode().is2xxSuccessful();
+        } catch (Exception e) {
+            logger.warn("Error in creating info consumer {} for rApp {}", consumerResource, rApp.getRappId(), e);
+            return false;
+        }
+    }
+
+    boolean deleteInfoProducer(Rapp rApp, String producerResource) {
+        logger.debug("Deleting DME info producer {} for rApp {}", producerResource, rApp.getRappId());
+        try {
+            ResponseEntity<Object> objectResponseEntity =
+                    dataProducerRegistrationApiClient.deleteInfoProducerWithHttpInfo(producerResource);
+            return objectResponseEntity.getStatusCode().is2xxSuccessful();
+        } catch (Exception e) {
+            logger.warn("Error in deleting info producer {} for rApp {}", producerResource, rApp.getRappId(), e);
+            return false;
+        }
+    }
+
+    boolean deleteInfoConsumer(Rapp rApp, String consumerResource) {
+        logger.debug("Deleting DME info consumer {} for rApp {}", consumerResource, rApp.getRappId());
+        try {
+            ResponseEntity<Object> objectResponseEntity =
+                    dataConsumerApiClient.deleteIndividualInfoJobWithHttpInfo(consumerResource);
+            return objectResponseEntity.getStatusCode().is2xxSuccessful();
+        } catch (Exception e) {
+            logger.warn("Error in deleting info consumer {} for rApp {}", consumerResource, rApp.getRappId(), e);
+            return false;
+        }
+    }
+}
diff --git a/rapp-manager-dme/src/main/resources/openapi/ics-api.yaml b/rapp-manager-dme/src/main/resources/openapi/ics-api.yaml
new file mode 100755 (executable)
index 0000000..6762bc5
--- /dev/null
@@ -0,0 +1,1669 @@
+openapi: 3.0.1
+info:
+  title: Data management and exposure
+  description: <h1>API documentation</h1><h2>General</h2><p>  The service is mainly
+    a broker between data producers and data consumers. A data producer has the ability
+    to produce one or several types of data (Information Type). One type of data can
+    be produced by zero to many producers. <br /><br />A data consumer can have several
+    active data subscriptions (Information Job). One Information Job consists of the
+    type of data to produce and additional parameters for filtering of the data. These
+    parameters are different for different data types.</p><h2>APIs provided by the
+    service</h2><h4>A1-EI</h4><p>  This API is between Near-RT RIC and the Non-RT
+    RIC.  The Near-RT RIC is a data consumer, which creates Information Jobs to subscribe
+    for data.  In this context, the information is referred to as 'Enrichment Information',
+    EI.</p><h4>Data producer API</h4><p>  This API is provided by the Non-RT RIC platform
+    and is intended to be part of the O-RAN R1 interface.  The API is for use by different
+    kinds of data producers and provides support for:<ul><li>Registry of supported
+    information types and which parameters needed to setup a subscription.</li><li>Registry
+    of existing data producers.</li><li>Callback API provided by producers to setup
+    subscriptions.</li></ul></p><h4>Data consumer API</h4><p>  This API is provided
+    by the Non-RT RIC platform and is intended to be part of the O-RAN R1 interface.  The
+    API is for use by different kinds of data consumers and provides support for:<ul><li>Querying
+    of available types of data to consume.</li><li>Management of data subscription
+    jobs</li><li>Optional callback API provided by consumers to get notification on
+    added and removed information types.</li></ul></p><h4>Service status</h4><p>  This
+    API provides a means to monitor the health of this service.</p>
+  license:
+    name: Copyright (C) 2020-2023 Nordix Foundation. Licensed under the Apache License.
+    url: http://www.apache.org/licenses/LICENSE-2.0
+  version: "1.0"
+servers:
+- url: /
+tags:
+- name: A1-EI (registration)
+  description: Data consumer EI job registration
+- name: A1-EI (callbacks)
+  description: Data consumer EI job status callbacks
+- name: Data consumer (callbacks)
+  description: API for data consumers
+- name: Data producer (registration)
+  description: API for data producers
+- name: Data producer (callbacks)
+  description: API implemented by data producers
+- name: Data consumer
+  description: API for data consumers
+- name: Service status
+  description: API for monitoring of the service
+- name: Actuator
+  description: Monitor and interact
+  externalDocs:
+    description: Spring Boot Actuator Web API Documentation
+    url: https://docs.spring.io/spring-boot/docs/current/actuator-api/html/
+- name: Authorization API
+  description: API used for authorization of information job access (this is provided
+    by an authorization producer such as OPA)
+paths:
+  /example-authz-check:
+    post:
+      tags:
+      - Authorization API
+      summary: Request for access authorization.
+      description: The authorization function decides if access is granted.
+      operationId: subscriptionAuth
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/subscription_authorization'
+        required: true
+      responses:
+        200:
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/authorization_result'
+  /data-producer/v1/info-types:
+    get:
+      tags:
+      - Data producer (registration)
+      summary: Info Type identifiers
+      operationId: getInfoTypdentifiers
+      responses:
+        200:
+          description: Info Type identifiers
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  type: string
+  /actuator/threaddump:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'threaddump'
+      operationId: threaddump
+      responses:
+        200:
+          description: OK
+          content:
+            text/plain;charset=UTF-8:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+  /A1-EI/v1/eitypes/{eiTypeId}:
+    get:
+      tags:
+      - A1-EI (registration)
+      summary: Individual EI type
+      operationId: getEiType
+      parameters:
+      - name: eiTypeId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: EI type
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/EiTypeObject'
+        404:
+          description: Enrichment Information type is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /data-producer/v1/info-types/{infoTypeId}:
+    get:
+      tags:
+      - Data producer (registration)
+      summary: Individual Information Type
+      operationId: getInfoType
+      parameters:
+      - name: infoTypeId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Info Type
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/producer_info_type_info'
+        404:
+          description: Information type is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    put:
+      tags:
+      - Data producer (registration)
+      summary: Individual Information Type
+      operationId: putInfoType
+      parameters:
+      - name: infoTypeId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/producer_info_type_info'
+        required: true
+      responses:
+        200:
+          description: Type updated
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        201:
+          description: Type created
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        400:
+          description: Input validation failed
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    delete:
+      tags:
+      - Data producer (registration)
+      summary: Individual Information Type
+      description: Existing jobs of the type will be automatically deleted.
+      operationId: deleteInfoType
+      parameters:
+      - name: infoTypeId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Not used
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        204:
+          description: Producer deleted
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        404:
+          description: Information type is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+        409:
+          description: The Information type has one or several active producers
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /data-consumer/v1/info-type-subscription/{subscriptionId}:
+    get:
+      tags:
+      - Data consumer
+      summary: Individual subscription for information types (registration/deregistration)
+      operationId: getIndividualTypeSubscription
+      parameters:
+      - name: subscriptionId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Type subscription
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/consumer_type_subscription_info'
+        404:
+          description: Subscription is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    put:
+      tags:
+      - Data consumer
+      summary: Individual subscription for information types (registration/deregistration)
+      description: This service operation is used to subscribe to notifications for
+        changes in the availability of data types.
+      operationId: putIndividualTypeSubscription
+      parameters:
+      - name: subscriptionId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/consumer_type_subscription_info'
+        required: true
+      responses:
+        200:
+          description: Subscription updated
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        201:
+          description: Subscription created
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+    delete:
+      tags:
+      - Data consumer
+      summary: Individual subscription for information types (registration/deregistration)
+      operationId: deleteIndividualTypeSubscription
+      parameters:
+      - name: subscriptionId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Not used
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        204:
+          description: Subscription deleted
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        404:
+          description: Subscription is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /example-dataproducer/health-check:
+    get:
+      tags:
+      - Data producer (callbacks)
+      summary: Producer supervision
+      description: The endpoint is provided by the Information Producer and is used
+        for supervision of the producer.
+      operationId: producerSupervision
+      responses:
+        200:
+          description: The producer is OK
+          content:
+            application/json:
+              schema:
+                type: string
+  /actuator/loggers:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'loggers'
+      operationId: loggers
+      responses:
+        200:
+          description: OK
+          content:
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+  /actuator/health/**:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'health-path'
+      operationId: health-path
+      responses:
+        200:
+          description: OK
+          content:
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+  /data-consumer/v1/info-types:
+    get:
+      tags:
+      - Data consumer
+      summary: Information type identifiers
+      operationId: getinfoTypeIdentifiers
+      responses:
+        200:
+          description: Information type identifiers
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  type: string
+  /example-dataconsumer/info-type-status:
+    post:
+      tags:
+      - Data consumer (callbacks)
+      summary: Callback for changed Information type registration status
+      description: The primitive is implemented by the data consumer and is invoked
+        when a Information type status has been changed. <br/>Subscription are managed
+        by primitives in 'Data consumer'
+      operationId: typeStatusCallback
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/consumer_type_registration_info'
+        required: true
+      responses:
+        200:
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+  /actuator/shutdown:
+    post:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'shutdown'
+      operationId: shutdown
+      responses:
+        200:
+          description: OK
+          content:
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+  /actuator/metrics/{requiredMetricName}:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'metrics-requiredMetricName'
+      operationId: metrics-requiredMetricName
+      parameters:
+      - name: requiredMetricName
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: OK
+          content:
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+  /actuator:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator root web endpoint
+      operationId: links
+      responses:
+        200:
+          description: OK
+          content:
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+                additionalProperties:
+                  type: object
+                  additionalProperties:
+                    $ref: '#/components/schemas/Link'
+            application/json:
+              schema:
+                type: object
+                additionalProperties:
+                  type: object
+                  additionalProperties:
+                    $ref: '#/components/schemas/Link'
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+                additionalProperties:
+                  type: object
+                  additionalProperties:
+                    $ref: '#/components/schemas/Link'
+  /data-consumer/v1/info-jobs:
+    get:
+      tags:
+      - Data consumer
+      summary: Information Job identifiers
+      description: query for information job identifiers
+      operationId: getJobIds
+      parameters:
+      - name: infoTypeId
+        in: query
+        description: selects subscription jobs of matching information type
+        required: false
+        style: form
+        explode: true
+        schema:
+          type: string
+      - name: owner
+        in: query
+        description: selects result for one owner
+        required: false
+        style: form
+        explode: true
+        schema:
+          type: string
+      responses:
+        200:
+          description: Information information job identifiers
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  type: string
+        404:
+          description: Information type is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    delete:
+      tags:
+      - Data consumer
+      summary: Information Jobs
+      description: delete all jobs for one owner
+      operationId: deleteJobsForOwner
+      parameters:
+      - name: owner
+        in: query
+        description: selects result for one owner
+        required: true
+        style: form
+        explode: true
+        schema:
+          type: string
+      responses:
+        204:
+          description: No Content
+          content:
+            application/json:
+              schema:
+                type: object
+  /actuator/loggers/{name}:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'loggers-name'
+      operationId: loggers-name
+      parameters:
+      - name: name
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: OK
+          content:
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+    post:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'loggers-name'
+      operationId: loggers-name_2
+      parameters:
+      - name: name
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+              enum:
+              - TRACE
+              - DEBUG
+              - INFO
+              - WARN
+              - ERROR
+              - FATAL
+              - OFF
+      responses:
+        200:
+          description: OK
+          content:
+            '*/*':
+              schema:
+                type: object
+  /example-dataproducer/info-job:
+    post:
+      tags:
+      - Data producer (callbacks)
+      summary: Callback for Information Job creation/modification
+      description: The call is invoked to activate or to modify a data subscription.
+        The endpoint is provided by the Information Producer.
+      operationId: jobCreatedCallback
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/producer_info_job_request'
+        required: true
+      responses:
+        200:
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+  /example-dataproducer/info-job/{infoJobId}:
+    delete:
+      tags:
+      - Data producer (callbacks)
+      summary: Callback for Information Job deletion
+      description: The call is invoked to terminate a data subscription. The endpoint
+        is provided by the Information Producer.
+      operationId: jobDeletedCallback
+      parameters:
+      - name: infoJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+  /A1-EI/v1/eijobs/{eiJobId}/status:
+    get:
+      tags:
+      - A1-EI (registration)
+      summary: EI job status
+      operationId: getEiJobStatus
+      parameters:
+      - name: eiJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: EI job status
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/EiJobStatusObject'
+        404:
+          description: Enrichment Information job is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /data-producer/v1/info-producers/{infoProducerId}/status:
+    get:
+      tags:
+      - Data producer (registration)
+      summary: Information producer status
+      operationId: getInfoProducerStatus
+      parameters:
+      - name: infoProducerId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Information producer status
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/producer_status'
+        404:
+          description: Information producer is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /data-consumer/v1/info-jobs/{infoJobId}/status:
+    get:
+      tags:
+      - Data consumer
+      summary: Job status
+      operationId: getInfoJobStatus
+      parameters:
+      - name: infoJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Information subscription job status
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/consumer_job_status'
+        404:
+          description: Information subscription job is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /actuator/metrics:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'metrics'
+      operationId: metrics
+      responses:
+        200:
+          description: OK
+          content:
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+  /example-dataconsumer/info-jobs/{infoJobId}/status:
+    post:
+      tags:
+      - A1-EI (callbacks)
+      summary: Callback for changed Information Job status
+      description: The primitive is implemented by the data consumer and is invoked
+        when a Information Job status has been changed.
+      operationId: jobStatusCallback
+      parameters:
+      - name: infoJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/EiJobStatusObject'
+        required: true
+      responses:
+        200:
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+  /actuator/info:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'info'
+      operationId: info
+      responses:
+        200:
+          description: OK
+          content:
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+  /A1-EI/v1/eitypes:
+    get:
+      tags:
+      - A1-EI (registration)
+      summary: EI type identifiers
+      operationId: getEiTypeIdentifiers
+      responses:
+        200:
+          description: EI type identifiers
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  type: string
+  /data-producer/v1/info-producers/{infoProducerId}:
+    get:
+      tags:
+      - Data producer (registration)
+      summary: Individual Information Producer
+      operationId: getInfoProducer
+      parameters:
+      - name: infoProducerId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Information producer
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/producer_registration_info'
+        404:
+          description: Information producer is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    put:
+      tags:
+      - Data producer (registration)
+      summary: Individual Information Producer
+      operationId: putInfoProducer
+      parameters:
+      - name: infoProducerId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/producer_registration_info'
+        required: true
+      responses:
+        200:
+          description: Producer updated
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        201:
+          description: Producer created
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        400:
+          description: Input validation failed
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+        404:
+          description: Producer type not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    delete:
+      tags:
+      - Data producer (registration)
+      summary: Individual Information Producer
+      operationId: deleteInfoProducer
+      parameters:
+      - name: infoProducerId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Not used
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        204:
+          description: Producer deleted
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        404:
+          description: Producer is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /status:
+    get:
+      tags:
+      - Service status
+      summary: Returns status and statistics of this service
+      operationId: getStatus
+      responses:
+        200:
+          description: Service is living
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/service_status_info'
+  /data-consumer/v1/info-type-subscription:
+    get:
+      tags:
+      - Data consumer
+      summary: Information type subscription identifiers
+      description: query for information type subscription identifiers
+      operationId: getInfoTypeSubscriptions
+      parameters:
+      - name: owner
+        in: query
+        description: selects result for one owner
+        required: false
+        style: form
+        explode: true
+        schema:
+          type: string
+      responses:
+        200:
+          description: Information type subscription identifiers
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  type: string
+  /A1-EI/v1/eijobs/{eiJobId}:
+    get:
+      tags:
+      - A1-EI (registration)
+      summary: Individual EI job
+      operationId: getIndividualEiJob
+      parameters:
+      - name: eiJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: EI job
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/EiJobObject'
+        404:
+          description: Enrichment Information job is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    put:
+      tags:
+      - A1-EI (registration)
+      summary: Individual EI job
+      description: If the requested info_type_id is not found, an attempt to find
+        a compatible version is made. As an example, 'type_1.9.0' is backwards compatible
+        with 'type_1.0.0'
+      operationId: putIndividualEiJob
+      parameters:
+      - name: eiJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/EiJobObject'
+        required: true
+      responses:
+        200:
+          description: Job updated
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        201:
+          description: Job created
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        400:
+          description: Input validation failed
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+        404:
+          description: Enrichment Information type is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+        409:
+          description: Cannot modify job type
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    delete:
+      tags:
+      - A1-EI (registration)
+      summary: Individual EI job
+      operationId: deleteIndividualEiJob
+      parameters:
+      - name: eiJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Not used
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        204:
+          description: Job deleted
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        404:
+          description: Enrichment Information job is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /actuator/logfile:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'logfile'
+      operationId: logfile
+      responses:
+        200:
+          description: OK
+          content:
+            text/plain;charset=UTF-8:
+              schema:
+                type: object
+  /data-consumer/v1/info-jobs/{infoJobId}:
+    get:
+      tags:
+      - Data consumer
+      summary: Individual data subscription job
+      operationId: getIndividualInfoJob
+      parameters:
+      - name: infoJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Information subscription job
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/consumer_job'
+        404:
+          description: Information subscription job is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    put:
+      tags:
+      - Data consumer
+      summary: Individual data subscription job
+      description: The job will be enabled when a producer is available. If the requested
+        info_type_id is not found, an attempt to find a compatible version is made.
+        As an example, 'type_1.9.0' is backwards compatible with 'type_1.0.0'
+      operationId: putIndividualInfoJob
+      parameters:
+      - name: infoJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/consumer_job'
+        required: true
+      responses:
+        200:
+          description: Job updated
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        201:
+          description: Job created
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        400:
+          description: Input validation failed
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+        404:
+          description: Information type is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+        409:
+          description: Cannot modify job type
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+    delete:
+      tags:
+      - Data consumer
+      summary: Individual data subscription job
+      operationId: deleteIndividualInfoJob
+      parameters:
+      - name: infoJobId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Not used
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        204:
+          description: Job deleted
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Void'
+        404:
+          description: Information subscription job is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /data-producer/v1/info-producers:
+    get:
+      tags:
+      - Data producer (registration)
+      summary: Information producer identifiers
+      operationId: getInfoProducerIdentifiers
+      parameters:
+      - name: infoTypeId
+        in: query
+        description: If given, only the producers for the Info Type is returned.
+        required: false
+        style: form
+        explode: true
+        schema:
+          type: string
+      responses:
+        200:
+          description: Information producer identifiers
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  type: string
+  /data-consumer/v1/info-types/{infoTypeId}:
+    get:
+      tags:
+      - Data consumer
+      summary: Individual information type
+      operationId: getInfoType_1
+      parameters:
+      - name: infoTypeId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Information type
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/consumer_information_type'
+        404:
+          description: Information type is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /actuator/health:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'health'
+      operationId: health
+      responses:
+        200:
+          description: OK
+          content:
+            application/vnd.spring-boot.actuator.v3+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
+            application/vnd.spring-boot.actuator.v2+json:
+              schema:
+                type: object
+  /A1-EI/v1/eijobs:
+    get:
+      tags:
+      - A1-EI (registration)
+      summary: EI job identifiers
+      description: query for EI job identifiers
+      operationId: getEiJobIds
+      parameters:
+      - name: eiTypeId
+        in: query
+        description: selects EI jobs of matching EI type
+        required: false
+        style: form
+        explode: true
+        schema:
+          type: string
+      - name: owner
+        in: query
+        description: selects EI jobs for one EI job owner
+        required: false
+        style: form
+        explode: true
+        schema:
+          type: string
+      responses:
+        200:
+          description: EI job identifiers
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  type: string
+        404:
+          description: Enrichment Information type is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /data-producer/v1/info-producers/{infoProducerId}/info-jobs:
+    get:
+      tags:
+      - Data producer (registration)
+      summary: Information Job definitions
+      description: Information Job definitions for one Information Producer
+      operationId: getInfoProducerJobs
+      parameters:
+      - name: infoProducerId
+        in: path
+        required: true
+        style: simple
+        explode: false
+        schema:
+          type: string
+      responses:
+        200:
+          description: Information producer
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/producer_info_job_request'
+        404:
+          description: Information producer is not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ProblemDetails'
+  /actuator/heapdump:
+    get:
+      tags:
+      - Actuator
+      summary: Actuator web endpoint 'heapdump'
+      operationId: heapdump
+      responses:
+        200:
+          description: OK
+          content:
+            application/octet-stream:
+              schema:
+                type: object
+components:
+  schemas:
+    consumer_information_type:
+      required:
+      - job_data_schema
+      - no_of_producers
+      - type_status
+      type: object
+      properties:
+        no_of_producers:
+          type: integer
+          description: The number of registered producers for the type
+          format: int32
+        type_status:
+          type: string
+          description: 'Allowed values: <br/>ENABLED: one or several producers for
+            the information type are available <br/>DISABLED: no producers for the
+            information type are available'
+          enum:
+          - ENABLED
+          - DISABLED
+        job_data_schema:
+          type: object
+          description: Json schema for the job data
+      description: Information for an Information type
+    EiTypeObject:
+      type: object
+      description: Information for an EI type
+    authorization_result:
+      required:
+      - result
+      type: object
+      properties:
+        result:
+          type: boolean
+          description: If true, the access is granted
+      description: Result of authorization
+    service_status_info:
+      required:
+      - no_of_jobs
+      - no_of_producers
+      - no_of_types
+      - status
+      type: object
+      properties:
+        no_of_producers:
+          type: integer
+          description: Number of Information Producers
+          format: int32
+        no_of_types:
+          type: integer
+          description: Number of Information Types
+          format: int32
+        no_of_jobs:
+          type: integer
+          description: Number of Information Jobs
+          format: int32
+        status:
+          type: string
+          description: status text
+    producer_registration_info:
+      required:
+      - info_job_callback_url
+      - info_producer_supervision_callback_url
+      - supported_info_types
+      type: object
+      properties:
+        info_producer_supervision_callback_url:
+          type: string
+          description: callback for producer supervision
+        supported_info_types:
+          type: array
+          description: Supported Information Type IDs
+          items:
+            type: string
+            description: Supported Information Type IDs
+        info_job_callback_url:
+          type: string
+          description: callback for Information Job
+      description: Information for an Information Producer
+    consumer_type_registration_info:
+      required:
+      - info_type_id
+      - job_data_schema
+      - status
+      type: object
+      properties:
+        info_type_id:
+          type: string
+          description: Information type identifier
+        job_data_schema:
+          type: object
+          description: Json schema for the job data
+        status:
+          type: string
+          description: 'Allowed values: <br/>REGISTERED: the information type has
+            been registered <br/>DEREGISTERED: the information type has been removed'
+          enum:
+          - REGISTERED
+          - DEREGISTERED
+      description: Information for an Information type
+    ProblemDetails:
+      type: object
+      properties:
+        detail:
+          type: string
+          description: A human-readable explanation specific to this occurrence of
+            the problem.
+          example: Information Job type not found
+        status:
+          type: integer
+          description: The HTTP status code generated by the origin server for this
+            occurrence of the problem.
+          format: int32
+          example: 404
+      description: A problem detail to carry details in a HTTP response according
+        to RFC 7807
+    EiJobStatusObject:
+      required:
+      - eiJobStatus
+      type: object
+      properties:
+        eiJobStatus:
+          type: string
+          description: 'Allowed values for EI job status: <br/>ENABLED: the A1-EI
+            producer is able to deliver EI result for the EI job <br/>DISABLED: the
+            A1-EI producer is unable to deliver EI result for the EI job'
+          enum:
+          - ENABLED
+          - DISABLED
+      description: Status for an EI job
+    consumer_job_status:
+      required:
+      - info_job_status
+      - producers
+      type: object
+      properties:
+        info_job_status:
+          type: string
+          description: 'Allowed values: <br/>ENABLED: the A1-Information producer
+            is able to deliver result for the Information Job <br/>DISABLED: the A1-Information
+            producer is unable to deliver result for the Information Job'
+          enum:
+          - ENABLED
+          - DISABLED
+        producers:
+          type: array
+          description: An array of all registered Information Producer Identifiers.
+          items:
+            type: string
+            description: An array of all registered Information Producer Identifiers.
+      description: Status for an Information Job
+    EiJobObject:
+      required:
+      - eiTypeId
+      - jobDefinition
+      - jobOwner
+      - jobResultUri
+      type: object
+      properties:
+        eiTypeId:
+          type: string
+          description: EI type Idenitifier of the EI job
+        jobResultUri:
+          type: string
+          description: The target URI of the EI data
+        jobOwner:
+          type: string
+          description: Identity of the owner of the job
+        statusNotificationUri:
+          type: string
+          description: The target of EI job status notifications
+        jobDefinition:
+          type: object
+          description: EI type specific job data
+      description: Information for an Enrichment Information Job
+    subscription_authorization:
+      required:
+      - input
+      type: object
+      properties:
+        input:
+          $ref: '#/components/schemas/input'
+      description: Authorization request for subscription requests
+    producer_info_type_info:
+      required:
+      - info_job_data_schema
+      type: object
+      properties:
+        info_type_information:
+          type: object
+          description: Type specific information for the information type
+        info_job_data_schema:
+          type: object
+          description: Json schema for the job data
+      description: Information for an Information Type
+    producer_info_job_request:
+      required:
+      - info_job_identity
+      type: object
+      properties:
+        owner:
+          type: string
+          description: The owner of the job. This is a string that indentifies the
+            job owner, which could be an application, a POD or something else.
+        last_updated:
+          type: string
+          description: The time when the job was last updated or created (ISO-8601)
+        info_job_identity:
+          type: string
+          description: Identity of the Information Job
+        target_uri:
+          type: string
+          description: URI for the target of the produced Information. Note, this
+            is deprecated and will be removed. The information on how the data is
+            delivered is type specific and should be defined in the type specific
+            info_job_data.
+        info_job_data:
+          type: object
+          description: Json for the job data
+        info_type_identity:
+          type: string
+          description: Type identity for the job
+      description: The body of the Information Producer callbacks for Information
+        Job creation and deletion
+    input:
+      required:
+      - access_type
+      - auth_token
+      - info_type_id
+      - job_definition
+      type: object
+      properties:
+        access_type:
+          type: string
+          description: Access type
+          enum:
+          - READ
+          - WRITE
+        info_type_id:
+          type: string
+          description: Information type identifier
+        job_definition:
+          type: object
+          description: Information type specific job data
+        auth_token:
+          type: string
+          description: Authorization token
+      description: input
+    consumer_job:
+      required:
+      - info_type_id
+      - job_definition
+      - job_owner
+      - job_result_uri
+      type: object
+      properties:
+        info_type_id:
+          type: string
+          description: Information type Idenitifier of the subscription job
+        job_result_uri:
+          type: string
+          description: The target URI of the subscribed information
+        job_owner:
+          type: string
+          description: Identity of the owner of the job
+        job_definition:
+          type: object
+          description: Information type specific job data
+        status_notification_uri:
+          type: string
+          description: The target of Information subscription job status notifications
+      description: Information for an Information Job
+    producer_status:
+      required:
+      - operational_state
+      type: object
+      properties:
+        operational_state:
+          type: string
+          description: Represents the operational states
+          enum:
+          - ENABLED
+          - DISABLED
+      description: Status for an Info Producer
+    Void:
+      type: object
+      description: 'Void/empty '
+    Link:
+      type: object
+      properties:
+        templated:
+          type: boolean
+        href:
+          type: string
+    consumer_type_subscription_info:
+      required:
+      - owner
+      - status_result_uri
+      type: object
+      properties:
+        owner:
+          type: string
+          description: Identity of the owner of the subscription
+        status_result_uri:
+          type: string
+          description: The target URI of the subscribed information
+      description: Information for an information type subscription
diff --git a/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/BeanTestConfiguration.java b/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/BeanTestConfiguration.java
new file mode 100755 (executable)
index 0000000..d22e939
--- /dev/null
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2023 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package com.oransc.rappmanager.dme.service;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.oransc.rappmanager.dme.configuration.DmeConfiguration;
+import com.oransc.rappmanager.dme.rest.DataConsumerApiClient;
+import com.oransc.rappmanager.dme.rest.DataProducerRegistrationApiClient;
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+
+@TestConfiguration
+@RequiredArgsConstructor
+public class BeanTestConfiguration {
+
+    private final DmeConfiguration dmeConfiguration;
+
+    @Bean
+    public ObjectMapper objectMapper() {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        return objectMapper;
+    }
+
+    @Bean
+    public RestTemplateBuilder restTemplateBuilder() {
+        return new RestTemplateBuilder();
+    }
+
+    @Bean
+    public CacheManager cacheManager() {
+        return new ConcurrentMapCacheManager(); // or any other CacheManager implementation you want to use in the test
+    }
+
+    @Bean
+    public RestTemplate restTemplate(RestTemplateBuilder builder) {
+        return builder.build();
+    }
+
+    @Bean
+    public com.oransc.rappmanager.dme.ApiClient dmeApiClient(RestTemplate restTemplate) {
+        com.oransc.rappmanager.dme.ApiClient apiClient = new com.oransc.rappmanager.dme.ApiClient(restTemplate);
+        return apiClient.setBasePath(dmeConfiguration.getBaseUrl());
+    }
+
+    @Bean
+    public DataProducerRegistrationApiClient dataProducerRegistrationApiClient(
+            com.oransc.rappmanager.dme.ApiClient apiClient) {
+        return new DataProducerRegistrationApiClient(apiClient);
+    }
+
+    @Bean
+    public DataConsumerApiClient dataConsumerApiClient(com.oransc.rappmanager.dme.ApiClient apiClient) {
+        return new DataConsumerApiClient(apiClient);
+    }
+}
diff --git a/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/DmeDeployerTest.java b/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/DmeDeployerTest.java
new file mode 100755 (executable)
index 0000000..5b97b2d
--- /dev/null
@@ -0,0 +1,350 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2023 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package com.oransc.rappmanager.dme.service;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.oransc.rappmanager.dme.configuration.DmeConfiguration;
+import com.oransc.rappmanager.models.cache.RappCacheService;
+import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler;
+import com.oransc.rappmanager.models.rapp.Rapp;
+import com.oransc.rappmanager.models.rapp.RappDmeResourceBuilder;
+import com.oransc.rappmanager.models.rapp.RappResources;
+import com.oransc.rappmanager.models.rapp.RappState;
+import com.oransc.rappmanager.models.rappinstance.RappInstance;
+import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine;
+import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachineConfig;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.client.ExpectedCount;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestTemplate;
+
+@SpringBootTest(classes = {DmeConfiguration.class, DmeDeployer.class, BeanTestConfiguration.class,
+        RappCsarConfigurationHandler.class, RappCacheService.class, RappInstanceStateMachineConfig.class,
+        RappInstanceStateMachine.class})
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@AutoConfigureMockMvc
+class DmeDeployerTest {
+
+    MockRestServiceServer mockServer;
+    @SpyBean
+    DmeDeployer dmeDeployer;
+    @Autowired
+    RestTemplate restTemplate;
+    @Autowired
+    DmeConfiguration dmeConfiguration;
+    @SpyBean
+    RappInstanceStateMachine rappInstanceStateMachine;
+
+    RappDmeResourceBuilder rappDmeResourceBuilder = new RappDmeResourceBuilder();
+
+    private static final String validRappFile = "valid-rapp-package.csar";
+    private static final String validRappFileNewInfoType = "valid-rapp-package-new-info-type.csar";
+    String validCsarFileLocation = "src/test/resources/";
+    ObjectMapper objectMapper = new ObjectMapper();
+
+    String URI_INFO_TYPES, URI_INFO_TYPE, URI_INFO_PRODUCER, URI_INFO_CONSUMER;
+
+    @BeforeAll
+    void initACMURI() {
+        URI_INFO_TYPES = dmeConfiguration.getBaseUrl() + "/data-producer/v1/info-types";
+        URI_INFO_TYPE = dmeConfiguration.getBaseUrl() + "/data-producer/v1/info-types/%s";
+        URI_INFO_PRODUCER = dmeConfiguration.getBaseUrl() + "/data-producer/v1/info-producers/%s";
+        URI_INFO_CONSUMER = dmeConfiguration.getBaseUrl() + "/data-consumer/v1/info-jobs/%s";
+    }
+
+    @BeforeEach
+    public void init() {
+        mockServer = MockRestServiceServer.createServer(restTemplate);
+    }
+
+    @ParameterizedTest
+    @MethodSource("getSuccessParamsWithUnavailableInfoTypes")
+    void testPrimeRappSuccessWithUnavailableInfoType(String rappFile, boolean result) throws JsonProcessingException {
+        RappResources rappResources = rappDmeResourceBuilder.getResources();
+        Rapp rapp = getRapp(Optional.empty());
+        rapp.setPackageName(rappFile);
+        rapp.setRappResources(rappResources);
+        List<String> infoTypes = List.of();
+        mockServer.expect(ExpectedCount.once(), requestTo(URI_INFO_TYPES)).andExpect(method(HttpMethod.GET)).andRespond(
+                withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
+                        .body(objectMapper.writeValueAsString(infoTypes)));
+        assertTrue(dmeDeployer.primeRapp(rapp));
+        if (rappFile.equals(validRappFileNewInfoType)) {
+            mockServer.verify();
+        }
+        assertEquals(rapp.getIsDmeValid(), result);
+    }
+
+    private static Stream<Arguments> getSuccessParamsWithUnavailableInfoTypes() {
+        return Stream.of(Arguments.of(validRappFile, true), Arguments.of(validRappFileNewInfoType, false));
+    }
+
+    @ParameterizedTest
+    @MethodSource("getSuccessParamsWithAvailableInfoTypes")
+    void testPrimeRappSuccessWithValidInfoType(String rappFile, boolean result) throws JsonProcessingException {
+        RappResources rappResources = rappDmeResourceBuilder.getResources();
+        Rapp rapp = getRapp(Optional.empty());
+        rapp.setPackageName(rappFile);
+        rapp.setRappResources(rappResources);
+        List<String> infoTypes = List.of("new-info-type-not-available");
+        mockServer.expect(ExpectedCount.once(), requestTo(URI_INFO_TYPES)).andExpect(method(HttpMethod.GET)).andRespond(
+                withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
+                        .body(objectMapper.writeValueAsString(infoTypes)));
+        assertTrue(dmeDeployer.primeRapp(rapp));
+        if (rappFile.equals(validRappFileNewInfoType)) {
+            mockServer.verify();
+        }
+        assertEquals(rapp.getIsDmeValid(), result);
+    }
+
+    private static Stream<Arguments> getSuccessParamsWithAvailableInfoTypes() {
+        return Stream.of(Arguments.of(validRappFile, true), Arguments.of(validRappFileNewInfoType, true));
+    }
+
+    @Test
+    void testPrimeRappFailure() {
+        RappResources rappResources = rappDmeResourceBuilder.getResources();
+        RappResources.DMEResources dme = rappResources.getDme();
+        Set<String> infoProducers = new HashSet<>(rappResources.getDme().getInfoProducers());
+        infoProducers.add("invalid-producer-not-available-in-rapp");
+        dme.setInfoProducers(infoProducers);
+        rappResources.setDme(dme);
+        Rapp rapp = getRapp(Optional.empty());
+        rapp.setRappResources(rappResources);
+        assertFalse(dmeDeployer.primeRapp(rapp));
+        assertFalse(rapp.getIsDmeValid());
+    }
+
+    @Test
+    void testDeprimeRapp() {
+        Rapp rapp = getRapp(Optional.empty());
+        rapp.setIsDmeValid(true);
+        assertTrue(dmeDeployer.deprimeRapp(rapp));
+        assertNull(rapp.getIsDmeValid());
+    }
+
+    @Test
+    void testDeployrAppInstanceSuccess() {
+        Rapp rapp = getRapp(Optional.empty());
+        RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance();
+        getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), true);
+        getMockServerClientCreateInfoProducer(rappInstance.getDme().getInfoProducer(), true);
+        getMockServerClientCreateInfoConsumer(rappInstance.getDme().getInfoConsumer(), true);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        assertTrue(dmeDeployer.deployRappInstance(rapp, rappInstance));
+        mockServer.verify();
+    }
+
+    @Test
+    void testDeployrAppInstanceSuccessWithoutConsumer() {
+        Rapp rapp = getRapp(Optional.empty());
+        RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance();
+        rappInstance.getDme().setInfoTypeConsumer(null);
+        rappInstance.getDme().setInfoConsumer(null);
+        getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), true);
+        getMockServerClientCreateInfoProducer(rappInstance.getDme().getInfoProducer(), true);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        assertTrue(dmeDeployer.deployRappInstance(rapp, rappInstance));
+        mockServer.verify();
+    }
+
+    @Test
+    void testDeployrAppInstanceWithoutProducer() {
+        Rapp rapp = getRapp(Optional.empty());
+        RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance();
+        rappInstance.getDme().setInfoTypesProducer(null);
+        rappInstance.getDme().setInfoProducer(null);
+        getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypeConsumer(), true);
+        getMockServerClientCreateInfoConsumer(rappInstance.getDme().getInfoConsumer(), true);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        assertTrue(dmeDeployer.deployRappInstance(rapp, rappInstance));
+        mockServer.verify();
+    }
+
+    @Test
+    void testDeployrAppInstanceFailureWithInfoType() {
+        Rapp rapp = getRapp(Optional.empty());
+        RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance();
+        getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), false);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        assertFalse(dmeDeployer.deployRappInstance(rapp, rappInstance));
+        mockServer.verify();
+    }
+
+    @Test
+    void testDeployrAppInstanceFailureWithInfoProducer() {
+        Rapp rapp = getRapp(Optional.empty());
+        RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance();
+        getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), true);
+        getMockServerClientCreateInfoProducer(rappInstance.getDme().getInfoProducer(), false);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        assertFalse(dmeDeployer.deployRappInstance(rapp, rappInstance));
+        mockServer.verify();
+    }
+
+    @Test
+    void testDeployrAppInstanceFailureWithInfoConsumer() {
+        Rapp rapp = getRapp(Optional.empty());
+        RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance();
+        getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), true);
+        getMockServerClientCreateInfoProducer(rappInstance.getDme().getInfoProducer(), true);
+        getMockServerClientCreateInfoConsumer(rappInstance.getDme().getInfoConsumer(), false);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        assertFalse(dmeDeployer.deployRappInstance(rapp, rappInstance));
+        mockServer.verify();
+    }
+
+    @Test
+    void testUndeployrAppInstanceSuccess() {
+        Rapp rapp = getRapp(Optional.empty());
+        rapp.setState(RappState.PRIMED);
+        RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance();
+        getMockServerClientDeleteInfoConsumer(rappInstance.getDme().getInfoConsumer(), true);
+        getMockServerClientDeleteInfoProducer(rappInstance.getDme().getInfoProducer(), true);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        assertTrue(dmeDeployer.undeployRappInstance(rapp, rappInstance));
+        mockServer.verify();
+    }
+
+
+    @Test
+    void testUndeployrAppInstanceFailureWithInfoProducer() {
+        Rapp rapp = getRapp(Optional.empty());
+        rapp.setState(RappState.PRIMED);
+        RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance();
+        getMockServerClientDeleteInfoConsumer(rappInstance.getDme().getInfoConsumer(), true);
+        getMockServerClientDeleteInfoProducer(rappInstance.getDme().getInfoProducer(), false);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        assertFalse(dmeDeployer.undeployRappInstance(rapp, rappInstance));
+        mockServer.verify();
+    }
+
+    @Test
+    void testUndeployrAppInstanceFailureWithInfoConsumer() {
+        Rapp rapp = getRapp(Optional.empty());
+        rapp.setState(RappState.PRIMED);
+        RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance();
+        getMockServerClientDeleteInfoConsumer(rappInstance.getDme().getInfoConsumer(), false);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        assertFalse(dmeDeployer.undeployRappInstance(rapp, rappInstance));
+        mockServer.verify();
+    }
+
+    @Test
+    void testCreateInfoTypeFailureInvalidInfoType() {
+        Rapp rapp = getRapp(Optional.empty());
+        assertFalse(dmeDeployer.createInfoTypes(rapp, null));
+    }
+
+    @Test
+    void testCreateInfoTypeFailureInvalidInfoProducer() {
+        Rapp rapp = getRapp(Optional.empty());
+        assertFalse(dmeDeployer.createInfoProducer(rapp, ""));
+    }
+
+    @Test
+    void testCreateInfoTypeFailureInvalidInfoConsumer() {
+        Rapp rapp = getRapp(Optional.empty());
+        assertFalse(dmeDeployer.createInfoConsumer(rapp, ""));
+    }
+
+    Rapp getRapp(Optional<UUID> rappOptional) {
+        return Rapp.builder().rappId(rappOptional.orElse(UUID.randomUUID())).name("").packageName(validRappFile)
+                       .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
+    }
+
+    void getMockServerClientCreateInfoType(String infoType, boolean isSuccess) {
+        if (isSuccess) {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_TYPE, infoType)))
+                    .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.CREATED));
+        } else {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_TYPE, infoType)))
+                    .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
+        }
+    }
+
+    void getMockServerClientCreateInfoProducer(String infoProducer, boolean isSuccess) {
+        if (isSuccess) {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_PRODUCER, infoProducer)))
+                    .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.CREATED));
+        } else {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_PRODUCER, infoProducer)))
+                    .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
+        }
+    }
+
+    void getMockServerClientCreateInfoConsumer(String infoConsumer, boolean isSuccess) {
+        if (isSuccess) {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_CONSUMER, infoConsumer)))
+                    .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.CREATED));
+        } else {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_CONSUMER, infoConsumer)))
+                    .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
+        }
+    }
+
+    void getMockServerClientDeleteInfoProducer(String infoProducer, boolean isSuccess) {
+        if (isSuccess) {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_PRODUCER, infoProducer)))
+                    .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.NO_CONTENT));
+        } else {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_PRODUCER, infoProducer)))
+                    .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
+        }
+    }
+
+    void getMockServerClientDeleteInfoConsumer(String infoConsumer, boolean isSuccess) {
+        if (isSuccess) {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_CONSUMER, infoConsumer)))
+                    .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.NO_CONTENT));
+        } else {
+            mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_CONSUMER, infoConsumer)))
+                    .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
+        }
+    }
+
+}
diff --git a/rapp-manager-dme/src/test/java/com/oransc/rappmanager/models/rapp/RappDmeResourceBuilder.java b/rapp-manager-dme/src/test/java/com/oransc/rappmanager/models/rapp/RappDmeResourceBuilder.java
new file mode 100755 (executable)
index 0000000..c12d981
--- /dev/null
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2023 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package com.oransc.rappmanager.models.rapp;
+
+import com.oransc.rappmanager.models.rappinstance.RappDMEInstance;
+import com.oransc.rappmanager.models.rappinstance.RappInstance;
+import java.util.Set;
+
+public class RappDmeResourceBuilder {
+
+    public RappResources getResources() {
+        RappResources rappResources = new RappResources();
+        RappResources.DMEResources dmeResources =
+                new RappResources.DMEResources(Set.of("json-file-data-from-filestore", "xml-file-data-from-filestore"),
+                        Set.of("json-file-data-producer", "xml-file-data-producer"),
+                        Set.of("json-file-consumer", "xml-file-consumer"));
+        rappResources.setDme(dmeResources);
+        return rappResources;
+    }
+
+    public RappInstance getRappInstance() {
+        RappInstance rappInstance = new RappInstance();
+        RappDMEInstance rappDMEInstance = new RappDMEInstance();
+        rappDMEInstance.setInfoTypesProducer(Set.of("json-file-data-from-filestore"));
+        rappDMEInstance.setInfoProducer("json-file-data-producer");
+        rappDMEInstance.setInfoTypeConsumer("json-file-data-from-filestore");
+        rappDMEInstance.setInfoConsumer("json-file-consumer");
+        rappInstance.setDme(rappDMEInstance);
+        return rappInstance;
+    }
+}
diff --git a/rapp-manager-dme/src/test/resources/application.yaml b/rapp-manager-dme/src/test/resources/application.yaml
new file mode 100755 (executable)
index 0000000..3c53841
--- /dev/null
@@ -0,0 +1,3 @@
+rappmanager:
+  dme:
+    baseurl: http://localhost:39390
diff --git a/rapp-manager-dme/src/test/resources/valid-rapp-package-new-info-type.csar b/rapp-manager-dme/src/test/resources/valid-rapp-package-new-info-type.csar
new file mode 100755 (executable)
index 0000000..67ae27c
Binary files /dev/null and b/rapp-manager-dme/src/test/resources/valid-rapp-package-new-info-type.csar differ
diff --git a/rapp-manager-dme/src/test/resources/valid-rapp-package.csar b/rapp-manager-dme/src/test/resources/valid-rapp-package.csar
new file mode 100755 (executable)
index 0000000..b3d75b2
Binary files /dev/null and b/rapp-manager-dme/src/test/resources/valid-rapp-package.csar differ
index 73c644e..05bf056 100755 (executable)
@@ -47,11 +47,12 @@ public class RappCsarConfigurationHandler {
     private static final String ACM_COMPOSITION_JSON_LOCATION = "Files/Acm/definition/compositions.json";
     private static final String ACM_DEFINITION_LOCATION = "Files/Acm/definition";
     private static final String ACM_INSTANCES_LOCATION = "Files/Acm/instances";
-
     private static final String SME_PROVIDER_FUNCS_LOCATION = "Files/Sme/providers";
     private static final String SME_SERVICE_APIS_LOCATION = "Files/Sme/serviceapis";
-
     private static final String SME_INVOKERS_LOCATION = "Files/Sme/invokers";
+    private static final String DME_INFO_TYPES_LOCATION = "Files/Dme/infotypes";
+    private static final String DME_INFO_PRODUCERS_LOCATION = "Files/Dme/infoproducers";
+    private static final String DME_INFO_CONSUMERS_LOCATION = "Files/Dme/infoconsumers";
 
 
     public boolean isValidRappPackage(MultipartFile multipartFile) {
@@ -88,7 +89,7 @@ public class RappCsarConfigurationHandler {
     }
 
     String getPayload(Rapp rapp, String location) {
-        logger.info("Getting payload for {} from {}", rapp.getRappId(), location);
+        logger.debug("Getting payload for {} from {}", rapp.getRappId(), location);
         File csarFile = getCsarFile(rapp);
         return getFileFromCsar(csarFile, location).toString();
     }
@@ -136,6 +137,18 @@ public class RappCsarConfigurationHandler {
                 getResourceUri(ACM_DEFINITION_LOCATION, rapp.getRappResources().getAcm().getCompositionDefinitions()));
     }
 
+    public String getDmeInfoProducerPayload(Rapp rapp, String producerIdentifier) {
+        return getPayload(rapp, getResourceUri(DME_INFO_PRODUCERS_LOCATION, producerIdentifier));
+    }
+
+    public String getDmeInfoTypePayload(Rapp rapp, String infoTypeIdentifier) {
+        return getPayload(rapp, getResourceUri(DME_INFO_TYPES_LOCATION, infoTypeIdentifier));
+    }
+
+    public String getDmeInfoConsumerPayload(Rapp rapp, String infoConsumerIdentifier) {
+        return getPayload(rapp, getResourceUri(DME_INFO_CONSUMERS_LOCATION, infoConsumerIdentifier));
+    }
+
     String getResourceUri(String resourceLocation, String resource) {
         return resourceLocation + "/" + resource + ".json";
     }
@@ -152,6 +165,10 @@ public class RappCsarConfigurationHandler {
                                 getFileListFromCsar(csarFile, SME_PROVIDER_FUNCS_LOCATION))
                                              .serviceApis(getFileListFromCsar(csarFile, SME_SERVICE_APIS_LOCATION))
                                              .invokers(getFileListFromCsar(csarFile, SME_INVOKERS_LOCATION)).build());
+                rappResources.setDme(RappResources.DMEResources.builder()
+                                             .infoTypes(getFileListFromCsar(csarFile, DME_INFO_TYPES_LOCATION))
+                                             .infoProducers(getFileListFromCsar(csarFile, DME_INFO_PRODUCERS_LOCATION))
+                                             .infoConsumers(getFileListFromCsar(csarFile, DME_INFO_CONSUMERS_LOCATION)).build());
             }
         } catch (Exception e) {
             logger.warn("Error in getting the rapp resources", e);
index 0aac806..a845fac 100755 (executable)
@@ -41,4 +41,5 @@ public class Rapp {
     Map<UUID, RappInstance> rappInstances = new HashMap<>();
 
     UUID compositionId;
+    Boolean isDmeValid;
 }
index 1bdc67d..7cb00be 100755 (executable)
@@ -28,5 +28,9 @@ public enum RappEvent {
     ACMUNDEPLOYED,
     SMEUNDEPLOYED,
     ACMUNDEPLOYFAILED,
-    SMEUNDEPLOYFAILED
+    SMEUNDEPLOYFAILED,
+    DMEDEPLOYED,
+    DMEDEPLOYFAILED,
+    DMEUNDEPLOYED,
+    DMEUNDEPLOYFAILED
 }
index a3033e2..d9344e4 100755 (executable)
@@ -27,6 +27,7 @@ public class RappResources {
 
     ACMResources acm;
     SMEResources sme;
+    DMEResources dme;
 
     @Data
     @Builder
@@ -44,4 +45,13 @@ public class RappResources {
         Set<String> serviceApis;
         Set<String> invokers;
     }
+
+    @Data
+    @Builder
+    public static class DMEResources {
+
+        Set<String> infoTypes;
+        Set<String> infoProducers;
+        Set<String> infoConsumers;
+    }
 }
diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappDMEInstance.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappDMEInstance.java
new file mode 100755 (executable)
index 0000000..bd1d19d
--- /dev/null
@@ -0,0 +1,31 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2023 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package com.oransc.rappmanager.models.rappinstance;
+
+import java.util.Set;
+import lombok.Data;
+
+@Data
+public class RappDMEInstance {
+
+    Set<String> infoTypesProducer;
+    String infoProducer;
+    String infoTypeConsumer;
+    String infoConsumer;
+}
index e1bc784..34c9a92 100755 (executable)
@@ -57,12 +57,18 @@ public class RappInstanceStateMachineConfig extends EnumStateMachineConfigurerAd
                 .withExternal()
                     .source(RappInstanceState.DEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.SMEDEPLOYFAILED)
                     .and()
+                .withExternal()
+                    .source(RappInstanceState.DEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.DMEDEPLOYFAILED)
+                    .and()
                 .withExternal()
                     .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.ACMUNDEPLOYFAILED)
                     .and()
                 .withExternal()
                     .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.SMEUNDEPLOYFAILED)
                     .and()
+                .withExternal()
+                    .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.DMEUNDEPLOYFAILED)
+                    .and()
                 .withExternal()
                     .source(RappInstanceState.DEPLOYED).target(RappInstanceState.UNDEPLOYING).event(RappEvent.UNDEPLOYING)
                     .and()
@@ -74,12 +80,20 @@ public class RappInstanceStateMachineConfig extends EnumStateMachineConfigurerAd
                     .source(RappInstanceState.DEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.SMEDEPLOYED)
                     .guard(deployedGuard())
                     .and()
+                .withExternal()
+                    .source(RappInstanceState.DEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.DMEDEPLOYED)
+                    .guard(deployedGuard())
+                    .and()
                 .withExternal()
                     .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.ACMUNDEPLOYED)
                     .guard(undeployedGuard())
                     .and()
                 .withExternal()
                     .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.SMEUNDEPLOYED)
+                    .guard(undeployedGuard())
+                    .and()
+                .withExternal()
+                    .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.DMEUNDEPLOYED)
                     .guard(undeployedGuard());
 
     }
@@ -90,7 +104,8 @@ public class RappInstanceStateMachineConfig extends EnumStateMachineConfigurerAd
         return stateContext -> {
             stateContext.getExtendedState().getVariables().put(stateContext.getEvent(), true);
             return stateContext.getExtendedState().getVariables().get(RappEvent.ACMDEPLOYED) != null
-                           && stateContext.getExtendedState().getVariables().get(RappEvent.SMEDEPLOYED) != null;
+                           && stateContext.getExtendedState().getVariables().get(RappEvent.SMEDEPLOYED) != null
+                           && stateContext.getExtendedState().getVariables().get(RappEvent.DMEDEPLOYED) != null;
         };
     }
 
@@ -99,7 +114,8 @@ public class RappInstanceStateMachineConfig extends EnumStateMachineConfigurerAd
         return stateContext -> {
             stateContext.getExtendedState().getVariables().put(stateContext.getEvent(), true);
             return stateContext.getExtendedState().getVariables().get(RappEvent.ACMUNDEPLOYED) != null
-                           && stateContext.getExtendedState().getVariables().get(RappEvent.SMEUNDEPLOYED) != null;
+                           && stateContext.getExtendedState().getVariables().get(RappEvent.SMEUNDEPLOYED) != null
+                           && stateContext.getExtendedState().getVariables().get(RappEvent.DMEUNDEPLOYED) != null;
         };
     }
 }
index 0a18ec4..d5e8d42 100755 (executable)
@@ -22,11 +22,14 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mock;
 
 import com.oransc.rappmanager.models.rapp.Rapp;
 import com.oransc.rappmanager.models.rapp.RappResources;
 import com.oransc.rappmanager.models.rappinstance.RappACMInstance;
+import com.oransc.rappmanager.models.rappinstance.RappDMEInstance;
 import com.oransc.rappmanager.models.rappinstance.RappSMEInstance;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -74,6 +77,18 @@ class RappCsarConfigurationHandlerTest {
         assertEquals(Boolean.FALSE, rappCsarConfigurationHandler.isValidRappPackage(multipartFile));
     }
 
+    @Test
+    void testCsarPackageValidationFailureWithoutOrginalName() throws IOException {
+        MultipartFile multipartFile = mock(MultipartFile.class);
+        assertEquals(Boolean.FALSE, rappCsarConfigurationHandler.isValidRappPackage(multipartFile));
+    }
+
+    @Test
+    void testInvalidCsarFileExist() {
+        MultipartFile multipartFile = mock(MultipartFile.class);
+        assertEquals(Boolean.FALSE, rappCsarConfigurationHandler.isFileExistsInCsar(multipartFile, "INVALID_LOCATION"));
+    }
+
     @Test
     void testCsarInstantiationPayload() throws JSONException {
         Rapp rapp = Rapp.builder().name("").packageName(validRappFile).packageLocation(validCsarFileLocation).build();
@@ -99,6 +114,13 @@ class RappCsarConfigurationHandlerTest {
         assertThat(fileListFromCsar).isEmpty();
     }
 
+    @Test
+    void testInvalidFileListingFromCsar() {
+        File file = new File("InvalidFile");
+        ByteArrayOutputStream fileByteArray = rappCsarConfigurationHandler.getFileFromCsar(file, null);
+        assertThat(fileByteArray.size()).isZero();
+    }
+
     @Test
     void testListResources() {
         UUID rappId = UUID.randomUUID();
@@ -108,10 +130,13 @@ class RappCsarConfigurationHandlerTest {
         RappResources rappResources = rappCsarConfigurationHandler.getRappResource(rapp);
         assertThat(rappResources).isNotNull();
         assertNotNull(rappResources.getAcm().getCompositionDefinitions());
-        assertThat(rappResources.getAcm().getCompositionInstances()).hasSize(3);
+        assertThat(rappResources.getAcm().getCompositionInstances()).hasSize(4);
         assertThat(rappResources.getSme().getProviderFunctions()).hasSize(4);
         assertThat(rappResources.getSme().getServiceApis()).hasSize(2);
         assertThat(rappResources.getSme().getInvokers()).hasSize(2);
+        assertThat(rappResources.getDme().getInfoTypes()).hasSize(2);
+        assertThat(rappResources.getDme().getInfoProducers()).hasSize(2);
+        assertThat(rappResources.getDme().getInfoConsumers()).hasSize(2);
     }
 
     @Test
@@ -122,6 +147,7 @@ class RappCsarConfigurationHandlerTest {
         assertThat(rappResources).isNotNull();
         assertNull(rappResources.getAcm());
         assertNull(rappResources.getSme());
+        assertNull(rappResources.getDme());
     }
 
     @Test
@@ -187,4 +213,43 @@ class RappCsarConfigurationHandlerTest {
         assertNotNull(smeProviderDomainPayload);
     }
 
+    @Test
+    void testGetDmeInfoTypePayload() {
+        UUID rappId = UUID.randomUUID();
+        RappDMEInstance rappDMEInstance = new RappDMEInstance();
+        rappDMEInstance.setInfoTypesProducer(Set.of("json-file-data-from-filestore"));
+        Rapp rapp =
+                Rapp.builder().rappId(rappId).name("").packageName(validRappFile).packageLocation(validCsarFileLocation)
+                        .build();
+        String dmeInfoTypePayload = rappCsarConfigurationHandler.getDmeInfoTypePayload(rapp,
+                rappDMEInstance.getInfoTypesProducer().iterator().next());
+        assertNotNull(dmeInfoTypePayload);
+    }
+
+    @Test
+    void testGetDmeInfoProducerPayload() {
+        UUID rappId = UUID.randomUUID();
+        RappDMEInstance rappDMEInstance = new RappDMEInstance();
+        rappDMEInstance.setInfoProducer("json-file-data-producer");
+        Rapp rapp =
+                Rapp.builder().rappId(rappId).name("").packageName(validRappFile).packageLocation(validCsarFileLocation)
+                        .build();
+        String dmeInfoProducerPayload =
+                rappCsarConfigurationHandler.getDmeInfoProducerPayload(rapp, rappDMEInstance.getInfoProducer());
+        assertNotNull(dmeInfoProducerPayload);
+    }
+
+    @Test
+    void testGetDmeInfoConsumerPayload() {
+        UUID rappId = UUID.randomUUID();
+        RappDMEInstance rappDMEInstance = new RappDMEInstance();
+        rappDMEInstance.setInfoConsumer("json-file-consumer");
+        Rapp rapp =
+                Rapp.builder().rappId(rappId).name("").packageName(validRappFile).packageLocation(validCsarFileLocation)
+                        .build();
+        String dmeInfoConsumerPayload =
+                rappCsarConfigurationHandler.getDmeInfoConsumerPayload(rapp, rappDMEInstance.getInfoConsumer());
+        assertNotNull(dmeInfoConsumerPayload);
+    }
+
 }
index 59d5c94..4657c76 100755 (executable)
@@ -75,7 +75,7 @@ class RappInstanceStateMachineConfigTest {
     }
 
     @ParameterizedTest
-    @EnumSource(value = RappEvent.class, names = {"ACMDEPLOYED", "SMEDEPLOYED"})
+    @EnumSource(value = RappEvent.class, names = {"ACMDEPLOYED", "SMEDEPLOYED", "DMEDEPLOYED" })
     void testIndividualDeployedState(RappEvent rappEvent) throws Exception {
         StateMachineTestPlan plan =
                 StateMachineTestPlanBuilder.<RappInstanceState, RappEvent>builder().stateMachine(stateMachine).step()
@@ -92,23 +92,51 @@ class RappInstanceStateMachineConfigTest {
                         .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
                         .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
                         .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
-                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
+                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
                         .and().build();
         plan.test();
     }
 
     @Test
-    void testDeployFailedState() throws Exception {
+    void testAcmDeployFailedState() throws Exception {
+        StateMachineTestPlan plan =
+                StateMachineTestPlanBuilder.<RappInstanceState, RappEvent>builder().stateMachine(stateMachine).step()
+                        .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
+                        .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
+                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.ACMDEPLOYFAILED).expectState(RappInstanceState.UNDEPLOYED)
+                        .expectStateChanged(1).and().build();
+        plan.test();
+    }
+
+    @Test
+    void testSmeDeployFailedState() throws Exception {
         StateMachineTestPlan plan =
                 StateMachineTestPlanBuilder.<RappInstanceState, RappEvent>builder().stateMachine(stateMachine).step()
                         .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
                         .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
                         .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
                         .sendEvent(RappEvent.SMEDEPLOYFAILED).expectState(RappInstanceState.UNDEPLOYED)
                         .expectStateChanged(1).and().build();
         plan.test();
     }
 
+    @Test
+    void testDmeDeployFailedState() throws Exception {
+        StateMachineTestPlan plan =
+                StateMachineTestPlanBuilder.<RappInstanceState, RappEvent>builder().stateMachine(stateMachine).step()
+                        .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
+                        .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
+                        .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYFAILED).expectState(RappInstanceState.UNDEPLOYED)
+                        .expectStateChanged(1).and().build();
+        plan.test();
+    }
+
     @Test
     void testUndeployingState() throws Exception {
         StateMachineTestPlan plan =
@@ -116,21 +144,23 @@ class RappInstanceStateMachineConfigTest {
                         .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
                         .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
                         .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
-                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
+                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
                         .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING)
                         .expectStateChanged(1).and().build();
         plan.test();
     }
 
     @ParameterizedTest
-    @EnumSource(value = RappEvent.class, names = {"ACMUNDEPLOYED", "SMEUNDEPLOYED"})
+    @EnumSource(value = RappEvent.class, names = {"ACMUNDEPLOYED", "SMEUNDEPLOYED", "DMEUNDEPLOYED" })
     void testIndividualUndeployedState(RappEvent rappEvent) throws Exception {
         StateMachineTestPlan plan =
                 StateMachineTestPlanBuilder.<RappInstanceState, RappEvent>builder().stateMachine(stateMachine).step()
                         .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
                         .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
                         .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
-                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
+                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
                         .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING)
                         .expectStateChanged(1).and().step().sendEvent(rappEvent)
                         .expectState(RappInstanceState.UNDEPLOYING).and().build();
@@ -144,26 +174,64 @@ class RappInstanceStateMachineConfigTest {
                         .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
                         .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
                         .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
-                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
+                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
                         .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING)
                         .expectStateChanged(1).and().step().sendEvent(RappEvent.ACMUNDEPLOYED)
                         .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.SMEUNDEPLOYED)
-                        .expectState(RappInstanceState.UNDEPLOYED).expectStateChanged(1).and().build();
+                        .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.DMEUNDEPLOYED)
+                        .expectStateChanged(1).and().build();
+        plan.test();
+    }
+
+    @Test
+    void testUndeployAcmFailedState() throws Exception {
+        StateMachineTestPlan plan =
+                StateMachineTestPlanBuilder.<RappInstanceState, RappEvent>builder().stateMachine(stateMachine).step()
+                        .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
+                        .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
+                        .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
+                        .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING)
+                        .expectStateChanged(1).and().step().sendEvent(RappEvent.SMEUNDEPLOYED)
+                        .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.DMEUNDEPLOYED)
+                        .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.ACMUNDEPLOYFAILED)
+                        .expectState(RappInstanceState.DEPLOYED).expectStateChanged(1).and().build();
         plan.test();
     }
 
     @Test
-    void testUndeployFailedState() throws Exception {
+    void testUndeploySmeFailedState() throws Exception {
         StateMachineTestPlan plan =
                 StateMachineTestPlanBuilder.<RappInstanceState, RappEvent>builder().stateMachine(stateMachine).step()
                         .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
                         .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
                         .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
-                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
+                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
                         .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING)
                         .expectStateChanged(1).and().step().sendEvent(RappEvent.ACMUNDEPLOYED)
+                        .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.DMEUNDEPLOYED)
                         .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.SMEUNDEPLOYFAILED)
                         .expectState(RappInstanceState.DEPLOYED).expectStateChanged(1).and().build();
         plan.test();
     }
+
+    @Test
+    void testUndeployDmeFailedState() throws Exception {
+        StateMachineTestPlan plan =
+                StateMachineTestPlanBuilder.<RappInstanceState, RappEvent>builder().stateMachine(stateMachine).step()
+                        .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING)
+                        .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step()
+                        .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step()
+                        .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1)
+                        .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING)
+                        .expectStateChanged(1).and().step().sendEvent(RappEvent.ACMUNDEPLOYED)
+                        .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.SMEUNDEPLOYED)
+                        .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.DMEUNDEPLOYFAILED)
+                        .expectState(RappInstanceState.DEPLOYED).expectStateChanged(1).and().build();
+        plan.test();
+    }
 }
index b8b36e3..b3d75b2 100755 (executable)
Binary files a/rapp-manager-models/src/test/resources/valid-rapp-package.csar and b/rapp-manager-models/src/test/resources/valid-rapp-package.csar differ
index 1760e9c..fc42f4d 100755 (executable)
@@ -21,12 +21,11 @@ package com.oransc.rappmanager.sme.service;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.oransc.rappmanager.models.rapp.Rapp;
-import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler;
 import com.oransc.rappmanager.models.RappDeployer;
+import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler;
+import com.oransc.rappmanager.models.rapp.Rapp;
 import com.oransc.rappmanager.models.rapp.RappEvent;
 import com.oransc.rappmanager.models.rappinstance.RappInstance;
-import com.oransc.rappmanager.models.cache.RappCacheService;
 import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine;
 import com.oransc.rappmanager.sme.invoker.data.APIInvokerEnrolmentDetails;
 import com.oransc.rappmanager.sme.provider.data.APIProviderEnrolmentDetails;
@@ -49,6 +48,7 @@ public class SmeDeployer implements RappDeployer {
 
     private final com.oransc.rappmanager.sme.provider.rest.DefaultApiClient providerDefaultApiClient;
 
+
     private final com.oransc.rappmanager.sme.publishservice.rest.DefaultApiClient publishServiceDefaultApiClient;
 
     private final com.oransc.rappmanager.sme.invoker.rest.DefaultApiClient invokerDefaultApiClient;
@@ -57,8 +57,6 @@ public class SmeDeployer implements RappDeployer {
 
     private final ObjectMapper objectMapper;
 
-    private final RappCacheService rappCacheService;
-
     private final RappInstanceStateMachine rappInstanceStateMachine;
 
     private String amfRegistrationId;
@@ -131,7 +129,7 @@ public class SmeDeployer implements RappDeployer {
 
     @Override
     public boolean deprimeRapp(Rapp rapp) {
-        //If there is any deprimgng operations
+        //If there is any depriming operations
         return true;
     }
 
@@ -190,7 +188,8 @@ public class SmeDeployer implements RappDeployer {
             if (providerApiPayload != null) {
                 ServiceAPIDescription serviceAPIDescription =
                         objectMapper.readValue(providerApiPayload, ServiceAPIDescription.class);
-                serviceAPIDescription.getAefProfiles().forEach(aefProfile -> aefProfile.setAefId(rappInstance.getSme().getAefId()));
+                serviceAPIDescription.getAefProfiles()
+                        .forEach(aefProfile -> aefProfile.setAefId(rappInstance.getSme().getAefId()));
                 ServiceAPIDescription serviceAPIDescriptionResponse =
                         publishServiceDefaultApiClient.postApfIdServiceApis(rappInstance.getSme().getApfId(),
                                 serviceAPIDescription);