Transform module information from NetConf node to Generic format for NCMP

Issue-ID: CPS-531
Signed-off-by: niamhcore <niamh.core@est.tech>
Change-Id: I918be4db5066d92b23e25fc7fbc22d4535fafc8c
diff --git a/docs/openapi/components.yml b/docs/openapi/components.yml
index 05f1ac0..3d8aa2c 100644
--- a/docs/openapi/components.yml
+++ b/docs/openapi/components.yml
@@ -43,6 +43,21 @@
             type: string
             example: system-001
 
+    ModuleSet:
+      type: object
+      properties:
+        schemas:
+          type: array
+          items:
+            type: object
+            properties:
+              moduleName:
+                type: string
+              revision:
+                type: string
+              namespace:
+                type: string
+
     OperationalRequest:
       type: object
       properties:
diff --git a/docs/openapi/openapi.yml b/docs/openapi/openapi.yml
index a4a238c..114c521 100644
--- a/docs/openapi/openapi.yml
+++ b/docs/openapi/openapi.yml
@@ -51,22 +51,16 @@
           content:
             application/json:
               schema:
-                type: string
+                $ref: 'components.yml#/components/schemas/ModuleSet'
                 example: {
-                            'schemas': {
-                              'schema': [
-                                {
-                                  'identifier': 'example-identifier',
-                                  'version': 'example-version',
-                                  'format': 'example-format',
-                                  'namespace': 'example:namespace',
-                                  'location': [
-                                    'example-location'
-                                  ]
-                                }
-                              ]
-                            }
-                          }
+                  "schemas": [
+                    {
+                      "moduleName": "example-identifier",
+                      "revision": "example-version",
+                      "namespace": "example-namespace"
+                    }
+                  ]
+                }
         '400':
           $ref: 'components.yml#/components/responses/BadRequest'
         '401':
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleSchemaList.java b/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleSchemaList.java
new file mode 100644
index 0000000..1e7dcb0
--- /dev/null
+++ b/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleSchemaList.java
@@ -0,0 +1,36 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.model;
+
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * ModuleSchemaList.
+ */
+@Getter
+@Setter
+public class ModuleSchemaList {
+
+    private List<ModuleSchemaProperties> schema;
+
+}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleSchemaProperties.java b/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleSchemaProperties.java
new file mode 100644
index 0000000..507758a
--- /dev/null
+++ b/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleSchemaProperties.java
@@ -0,0 +1,40 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.model;
+
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * ModuleSchemaProperties.
+ */
+@Getter
+@Setter
+public class ModuleSchemaProperties {
+
+    private String identifier;
+    private String version;
+    private String format;
+    private String namespace;
+    private List<String> location;
+
+}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleSchemas.java b/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleSchemas.java
new file mode 100644
index 0000000..1794ded
--- /dev/null
+++ b/src/main/java/org/onap/cps/ncmp/dmi/model/ModuleSchemas.java
@@ -0,0 +1,34 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * ModuleSchemas.
+ */
+@Getter
+@Setter
+public class ModuleSchemas {
+
+    private ModuleSchemaList schemas;
+}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java b/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java
index 969e08d..3d49b78 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/rest/controller/DmiRestController.java
@@ -28,6 +28,7 @@
 import org.onap.cps.ncmp.dmi.model.CmHandles;
 import org.onap.cps.ncmp.dmi.model.ModuleReference;
 import org.onap.cps.ncmp.dmi.model.ModuleRequestParent;
+import org.onap.cps.ncmp.dmi.model.ModuleSet;
 import org.onap.cps.ncmp.dmi.model.OperationalRequest;
 import org.onap.cps.ncmp.dmi.rest.api.DmiPluginApi;
 import org.onap.cps.ncmp.dmi.rest.api.DmiPluginInternalApi;
@@ -52,10 +53,9 @@
     }
 
     @Override
-    public ResponseEntity<String> getModulesForCmHandle(final String cmHandle) {
-
-        final String modulesListAsJson = dmiService.getModulesForCmHandle(cmHandle);
-        return new ResponseEntity<>(modulesListAsJson, HttpStatus.OK);
+    public ResponseEntity<ModuleSet> getModulesForCmHandle(final String cmHandle) {
+        final var moduleSet = dmiService.getModulesForCmHandle(cmHandle);
+        return new ResponseEntity<ModuleSet>(moduleSet, HttpStatus.OK);
     }
 
     @Override
@@ -118,6 +118,7 @@
 
     private List<ModuleReference> convertRestObjectToJavaApiObject(final ModuleRequestParent moduleRequestParent) {
         return objectMapper
-            .convertValue(moduleRequestParent.getData().getModules(), new TypeReference<List<ModuleReference>>() {});
+            .convertValue(moduleRequestParent.getData().getModules(), new TypeReference<List<ModuleReference>>() {
+            });
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java
index 528eb64..a7c9434 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiService.java
@@ -25,6 +25,7 @@
 import javax.validation.constraints.NotNull;
 import org.onap.cps.ncmp.dmi.exception.DmiException;
 import org.onap.cps.ncmp.dmi.model.ModuleReference;
+import org.onap.cps.ncmp.dmi.model.ModuleSet;
 
 /**
  * Interface for handling Dmi plugin Data.
@@ -38,7 +39,7 @@
      * @return {@code String} returns all modules
      * @throws DmiException can throw dmi exception
      */
-    String getModulesForCmHandle(String cmHandle) throws DmiException;
+    ModuleSet getModulesForCmHandle(String cmHandle) throws DmiException;
 
     /**
      * This method used to register the given {@code CmHandles}
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
index 32c8526..94bd97d 100644
--- a/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
+++ b/src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
@@ -39,6 +39,10 @@
 import org.onap.cps.ncmp.dmi.model.CmHandleOperation;
 import org.onap.cps.ncmp.dmi.model.CreatedCmHandle;
 import org.onap.cps.ncmp.dmi.model.ModuleReference;
+import org.onap.cps.ncmp.dmi.model.ModuleSchemaProperties;
+import org.onap.cps.ncmp.dmi.model.ModuleSchemas;
+import org.onap.cps.ncmp.dmi.model.ModuleSet;
+import org.onap.cps.ncmp.dmi.model.ModuleSetSchemas;
 import org.onap.cps.ncmp.dmi.service.client.NcmpRestClient;
 import org.onap.cps.ncmp.dmi.service.operation.SdncOperations;
 import org.springframework.http.HttpStatus;
@@ -73,14 +77,15 @@
     }
 
     @Override
-    public String getModulesForCmHandle(final String cmHandle) throws DmiException {
+    public ModuleSet getModulesForCmHandle(final String cmHandle) throws DmiException {
         final ResponseEntity<String> responseEntity = sdncOperations.getModulesFromNode(cmHandle);
         if (responseEntity.getStatusCode() == HttpStatus.OK) {
             final String responseBody = responseEntity.getBody();
             if (StringUtils.isEmpty(responseBody)) {
                 throw new ModulesNotFoundException(cmHandle, "SDNC returned no modules for given cm-handle.");
             }
-            return responseBody;
+            final ModuleSet moduleSet = createModuleSchema(responseBody);
+            return moduleSet;
         } else {
             throw new DmiException("SDNC is not able to process request.",
                     "response code : " + responseEntity.getStatusCode() + " message : " + responseEntity.getBody());
@@ -129,6 +134,36 @@
         }
     }
 
+    private ModuleSet createModuleSchema(final String responseBody) {
+        final var moduleSchemas = convertModulesToNodeSchema(responseBody);
+        final List<ModuleSetSchemas> moduleSetSchemas = new ArrayList<>();
+        for (final ModuleSchemaProperties schemaProperties : moduleSchemas.getSchemas().getSchema()) {
+            moduleSetSchemas.add(convertModulesToModuleSchemas(schemaProperties));
+        }
+        final var moduleSet = new ModuleSet();
+        moduleSet.setSchemas(moduleSetSchemas);
+        return moduleSet;
+    }
+
+    private ModuleSetSchemas convertModulesToModuleSchemas(final ModuleSchemaProperties moduleSchemaProperties) {
+        final var moduleSetSchemas = new ModuleSetSchemas();
+        moduleSetSchemas.setModuleName(moduleSchemaProperties.getIdentifier());
+        moduleSetSchemas.setNamespace(moduleSchemaProperties.getNamespace());
+        moduleSetSchemas.setRevision(moduleSchemaProperties.getVersion());
+        return moduleSetSchemas;
+    }
+
+    private ModuleSchemas convertModulesToNodeSchema(final String modulesListAsJson) {
+        try {
+            return objectMapper.readValue(modulesListAsJson, ModuleSchemas.class);
+        } catch (final JsonProcessingException e) {
+            log.error("JSON exception occurred when converting the following modules to node schema "
+                + "{}", modulesListAsJson);
+            throw new DmiException("Unable to process JSON.",
+                "JSON exception occurred when mapping modules.", e);
+        }
+    }
+
     @Override
     public Object getResourceDataOperationalForCmHandle(final @NotNull String cmHandle,
                                              final @NotNull String resourceIdentifier,
diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy
index 0c8ae3f..f24f65a 100644
--- a/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy
+++ b/src/test/groovy/org/onap/cps/ncmp/dmi/rest/controller/DmiRestControllerSpec.groovy
@@ -20,11 +20,19 @@
 
 package org.onap.cps.ncmp.dmi.rest.controller
 
+
+import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.ncmp.dmi.TestUtils
 import org.onap.cps.ncmp.dmi.exception.DmiException
 import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException
 import org.onap.cps.ncmp.dmi.exception.ModulesNotFoundException
+
 import org.onap.cps.ncmp.dmi.model.ModuleReference
+import org.onap.cps.ncmp.dmi.model.ModuleSchemaList
+import org.onap.cps.ncmp.dmi.model.ModuleSchemaProperties
+import org.onap.cps.ncmp.dmi.model.ModuleSchemas
+import org.onap.cps.ncmp.dmi.model.ModuleSet
+import org.onap.cps.ncmp.dmi.model.ModuleSetSchemas
 import org.onap.cps.ncmp.dmi.service.DmiService
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
@@ -47,6 +55,9 @@
     @SpringBean
     DmiService mockDmiService = Mock()
 
+    @SpringBean
+    ObjectMapper mockObjectMapper = Spy()
+
     @Autowired
     private MockMvc mvc
 
@@ -57,8 +68,14 @@
         given: 'REST endpoint for getting all modules'
             def getModuleUrl = "$basePathV1/ch/node1/modules"
         and: 'get modules for cm-handle returns a json'
-            def someJson = 'some-json'
-            mockDmiService.getModulesForCmHandle('node1') >> someJson
+            def moduleSetSchema = new ModuleSetSchemas()
+            moduleSetSchema.namespace('some-namespace')
+            moduleSetSchema.moduleName('some-moduleName')
+            moduleSetSchema.revision('some-revision')
+            def moduleSetSchemasList = [moduleSetSchema] as List<ModuleSetSchemas>
+            def moduleSet = new ModuleSet()
+            moduleSet.schemas(moduleSetSchemasList)
+            mockDmiService.getModulesForCmHandle('node1') >> moduleSet
         when: 'post is being called'
             def response = mvc.perform(post(getModuleUrl)
                     .contentType(MediaType.APPLICATION_JSON))
@@ -66,7 +83,21 @@
         then: 'status is OK'
             response.status == HttpStatus.OK.value()
         and: 'the response content matches the result from the DMI service'
-            response.getContentAsString() == someJson
+            response.getContentAsString() == '{"schemas":[{"moduleName":"some-moduleName","revision":"some-revision","namespace":"some-namespace"}]}'
+    }
+
+    def 'Get all modules for given cm handle with invalid json.'() {
+        given: 'REST endpoint for getting all modules'
+            def getModuleUrl = "$basePathV1/ch/node1/modules"
+        and: 'get modules for cmHandle throws an exception'
+            mockObjectMapper.readValue(_ as String, _ as ModuleSchemaList) >> { throw Mock(DmiException.class) }
+            mockDmiService.getModulesForCmHandle('node1') >> 'some-value'
+        when: 'post is being called'
+            def response = mvc.perform(post(getModuleUrl)
+                    .contentType(MediaType.APPLICATION_JSON))
+                    .andReturn().response
+        then: 'the status is as expected'
+            response.status == HttpStatus.INTERNAL_SERVER_ERROR.value()
     }
 
     def 'Get all modules for given cm handle with exception handling of #scenario.'() {
@@ -75,16 +106,16 @@
         and: 'get modules for cm-handle throws #exceptionClass'
             mockDmiService.getModulesForCmHandle('node1') >> { throw Mock(exceptionClass) }
         when: 'post is invoked'
-            def response = mvc.perform(post(getModuleUrl)
+            def response = mvc.perform( post(getModuleUrl)
                     .contentType(MediaType.APPLICATION_JSON))
                     .andReturn().response
         then: 'response status is #expectedResponse'
             response.status == expectedResponse
         where: 'the scenario is #scenario'
-            scenario                      | exceptionClass                 || expectedResponse
-            'dmi service exception'       | DmiException.class             || HttpStatus.INTERNAL_SERVER_ERROR.value()
-            'no modules found'            | ModulesNotFoundException.class || HttpStatus.NOT_FOUND.value()
-            'any other runtime exception' | RuntimeException.class         || HttpStatus.INTERNAL_SERVER_ERROR.value()
+            scenario                       |  exceptionClass                 || expectedResponse
+            'dmi service exception'        |  DmiException.class             || HttpStatus.INTERNAL_SERVER_ERROR.value()
+            'no modules found'             |  ModulesNotFoundException.class || HttpStatus.NOT_FOUND.value()
+            'any other runtime exception'  |  RuntimeException.class         || HttpStatus.INTERNAL_SERVER_ERROR.value()
     }
 
     def 'Register given list of cm handles.'() {
diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy
index da8ff6b..e456d41 100644
--- a/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy
+++ b/src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy
@@ -22,6 +22,7 @@
 
 import com.fasterxml.jackson.core.JsonProcessingException
 import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.dmi.TestUtils
 import org.onap.cps.ncmp.dmi.config.DmiPluginConfig
 import org.onap.cps.ncmp.dmi.exception.CmHandleRegistrationException
 import org.onap.cps.ncmp.dmi.exception.DmiException
@@ -29,6 +30,8 @@
 import org.onap.cps.ncmp.dmi.exception.ModulesNotFoundException
 import org.onap.cps.ncmp.dmi.exception.ResourceDataNotFound
 import org.onap.cps.ncmp.dmi.model.ModuleReference
+import org.onap.cps.ncmp.dmi.model.ModuleSchemaList
+import org.onap.cps.ncmp.dmi.model.ModuleSchemas
 import org.onap.cps.ncmp.dmi.service.client.NcmpRestClient
 import org.onap.cps.ncmp.dmi.service.operation.SdncOperations
 import org.springframework.http.HttpStatus
@@ -49,12 +52,25 @@
         given: 'cm handle id'
             def cmHandle = 'node1'
         and: 'request operation returns OK'
-            def body = 'body'
+            def body = TestUtils.getResourceFileContent('ModuleSchema.json')
             mockSdncOperations.getModulesFromNode(cmHandle) >> new ResponseEntity<String>(body, HttpStatus.OK)
         when: 'get modules for cm-handle is called'
             def result = objectUnderTest.getModulesForCmHandle(cmHandle)
         then: 'result is equal to the response from the SDNC service'
-            result == body
+            result.toString().contains('moduleName: example-identifier')
+            result.toString().contains('revision: example-version')
+    }
+
+    def 'Call get modules for cm-handle with invalid json.'() {
+        given: 'cm handle id'
+            def cmHandle = 'node1'
+        and: 'request operation returns invalid json'
+            def body = TestUtils.getResourceFileContent('ModuleSchema.json')
+            mockSdncOperations.getModulesFromNode(cmHandle) >> new ResponseEntity<String>('invalid json', HttpStatus.OK)
+        when: 'get modules for cm-handle is called'
+            def result = objectUnderTest.getModulesForCmHandle(cmHandle)
+        then: 'a dmi exception is thrown'
+           thrown(DmiException)
     }
 
     def 'Call get modules for cm-handle and SDNC returns "bad request" status.'() {
diff --git a/src/test/resources/ModuleSchema.json b/src/test/resources/ModuleSchema.json
new file mode 100644
index 0000000..07a0a03
--- /dev/null
+++ b/src/test/resources/ModuleSchema.json
@@ -0,0 +1,15 @@
+{
+  "schemas": {
+    "schema": [
+      {
+        "identifier": "example-identifier",
+        "version": "example-version",
+        "format": "example-format",
+        "namespace": "example:namespace",
+        "location": [
+          "example-location"
+        ]
+      }
+    ]
+  }
+}
\ No newline at end of file