Changing putOperationWithJson to postOperationWithJson 73/126173/15
authorJosephKeenan <joseph.keenan@est.tech>
Wed, 8 Dec 2021 18:16:44 +0000 (18:16 +0000)
committerJosephKeenan <joseph.keenan@est.tech>
Tue, 14 Dec 2021 16:13:40 +0000 (16:13 +0000)
-Modified responseEntity<String> to reesponseEntity<Object>
-Changed behaviour of sync to use new repsonses and removed JSON parsing
-Updated tests to use new responsess
-Tests have been updated and added jira to docs
-Added messageConverters to RestTemplate to support plain text with
ResponseEntity<Object>
-Added docker log output for cps & dmi containers during CSIT teardown
-Moved reponse conversion from service into DMIModelOperations class
-Added response handling test (edgecases)
-Updated response request body for passthrough-running CSIT test to pass

Issue-ID: CPS-777
Signed-off-by: JosephKeenan <joseph.keenan@est.tech>
Change-Id: If2acf83a97b8aad5aa2c342154d807a47cace6a0

13 files changed:
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
csit/plans/cps/teardown.sh
csit/tests/ncmp-passthrough/ncmp-passthrough.robot
docs/release-notes.rst

index faa2efe..bd86cd0 100755 (executable)
@@ -28,10 +28,6 @@ import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -52,7 +48,6 @@ import org.onap.cps.ncmp.api.models.CmHandle;
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
 import org.onap.cps.ncmp.api.models.PersistenceCmHandle;
 import org.onap.cps.ncmp.api.models.PersistenceCmHandlesList;
-import org.onap.cps.ncmp.api.models.YangResource;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
 import org.onap.cps.spi.exceptions.DataValidationException;
@@ -308,9 +303,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     }
 
     private void syncAndCreateSchemaSet(final PersistenceCmHandle persistenceCmHandle) {
-
         final List<ModuleReference> moduleReferencesFromCmHandle =
-            toModuleReferences(dmiModelOperations.getModuleReferences(persistenceCmHandle));
+            dmiModelOperations.getModuleReferences(persistenceCmHandle);
         final List<ModuleReference> existingModuleReferences = new ArrayList<>();
         final List<ModuleReference> unknownModuleReferences = new ArrayList<>();
         prepareModuleSubsets(moduleReferencesFromCmHandle, existingModuleReferences, unknownModuleReferences);
@@ -319,7 +313,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         if (unknownModuleReferences.isEmpty()) {
             newYangResourcesModuleNameToContentMap = new HashMap<>();
         } else {
-            newYangResourcesModuleNameToContentMap = getNewYangResourcesFromDmi(persistenceCmHandle,
+            newYangResourcesModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle,
                 unknownModuleReferences);
         }
         cpsModuleService
@@ -348,47 +342,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
             persistenceCmHandle.getId());
     }
 
-    private Map<String, String> getNewYangResourcesFromDmi(final PersistenceCmHandle persistenceCmHandle,
-                                                           final List<ModuleReference> unknownModuleReferences) {
-        final ResponseEntity<String> responseEntity =
-            dmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle, unknownModuleReferences);
-
-        final JsonArray moduleResources = new Gson().fromJson(responseEntity.getBody(),
-            JsonArray.class);
-        final Map<String, String> newYangResourcesModuleNameToContentMap = new HashMap<>();
-
-        for (final JsonElement moduleResource : moduleResources) {
-            final YangResource yangResource = toYangResource((JsonObject) moduleResource);
-            newYangResourcesModuleNameToContentMap.put(yangResource.getModuleName(), yangResource.getYangSource());
-        }
-        return newYangResourcesModuleNameToContentMap;
-    }
-
-    private static YangResource toYangResource(final JsonObject yangResourceAsJson) {
-        final YangResource yangResource = new YangResource();
-        yangResource.setModuleName(yangResourceAsJson.get("moduleName").getAsString());
-        yangResource.setRevision(yangResourceAsJson.get("revision").getAsString());
-        yangResource.setYangSource(yangResourceAsJson.get("yangSource").getAsString());
-        return yangResource;
-    }
 
-    private static List<ModuleReference> toModuleReferences(
-            final ResponseEntity<String> dmiFetchModulesResponseEntity) {
-        final List<ModuleReference> moduleReferences = new ArrayList<>();
-        final JsonObject bodyAsJsonObject = new Gson().fromJson(dmiFetchModulesResponseEntity.getBody(),
-            JsonObject.class);
-        final JsonArray moduleReferencesAsJson = bodyAsJsonObject.getAsJsonArray("schemas");
-        for (final JsonElement moduleReferenceAsJson : moduleReferencesAsJson) {
-            final ModuleReference moduleReference = toModuleReference((JsonObject) moduleReferenceAsJson);
-            moduleReferences.add(moduleReference);
-        }
-        return moduleReferences;
-    }
 
-    private static ModuleReference toModuleReference(final JsonObject moduleReferenceAsJson) {
-        final ModuleReference moduleReference = new ModuleReference();
-        moduleReference.setModuleName(moduleReferenceAsJson.get("moduleName").getAsString());
-        moduleReference.setRevision(moduleReferenceAsJson.get("revision").getAsString());
-        return moduleReference;
-    }
 }
index 9f3df6b..94faa55 100644 (file)
@@ -53,11 +53,11 @@ public class DmiRestClient {
      * @param httpHeaders http headers
      * @return response entity of type String
      */
-    public ResponseEntity<String> postOperationWithJsonData(final String dmiResourceUrl,
+    public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl,
                                                             final String jsonData,
                                                             final HttpHeaders httpHeaders) {
         final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(httpHeaders));
-        return restTemplate.postForEntity(dmiResourceUrl, httpEntity, String.class);
+        return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class);
     }
 
     private HttpHeaders configureHttpHeaders(final HttpHeaders httpHeaders) {
@@ -72,8 +72,8 @@ public class DmiRestClient {
      * @param httpHeaders http headers
      * @return response entity of type String
      */
-    public ResponseEntity<String> postOperation(final String dmiResourceUrl, final HttpHeaders httpHeaders) {
+    public ResponseEntity<Object> postOperation(final String dmiResourceUrl, final HttpHeaders httpHeaders) {
         final var httpEntity = new HttpEntity<>(configureHttpHeaders(httpHeaders));
-        return restTemplate.exchange(dmiResourceUrl, HttpMethod.POST, httpEntity, String.class);
+        return restTemplate.exchange(dmiResourceUrl, HttpMethod.POST, httpEntity, Object.class);
     }
 }
index 81c9dff..60b44c2 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.config;
 
+import java.util.Arrays;
 import lombok.Getter;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Scope;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.stereotype.Component;
 import org.springframework.web.client.RestTemplate;
 
@@ -42,8 +46,25 @@ public class NcmpConfiguration {
         private String dmiBasePath;
     }
 
+    /**
+     * Rest template bean.
+     *
+     * @param restTemplateBuilder the rest template builder
+     * @return rest template instance
+     */
     @Bean
+    @Scope("singleton")
     public static RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder) {
-        return restTemplateBuilder.build();
+        final RestTemplate restTemplate = restTemplateBuilder.build();
+        setRestTemplateMessageConverters(restTemplate);
+        return restTemplate;
+    }
+
+    private static void setRestTemplateMessageConverters(final RestTemplate restTemplate) {
+        final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter =
+            new MappingJackson2HttpMessageConverter();
+        mappingJackson2HttpMessageConverter.setSupportedMediaTypes(
+            Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN));
+        restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
     }
 }
index eccb9a0..095f677 100644 (file)
@@ -62,7 +62,7 @@ public class DmiDataOperations extends DmiOperations {
      * @param dataStore  data store enum
      * @return {@code ResponseEntity} response entity
      */
-    public ResponseEntity<String> getResourceDataFromDmi(final String cmHandle,
+    public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandle,
                                                           final String resourceId,
                                                           final String optionsParamInQuery,
                                                           final String acceptParamInHeader,
@@ -93,7 +93,7 @@ public class DmiDataOperations extends DmiOperations {
      * @param dataType    data type
      * @return {@code ResponseEntity} response entity
      */
-    public ResponseEntity<String> writeResourceDataPassThroughRunningFromDmi(final String cmHandle,
+    public ResponseEntity<Object> writeResourceDataPassThroughRunningFromDmi(final String cmHandle,
                                                                              final String resourceId,
                                                                              final OperationEnum operation,
                                                                              final String requestData,
index c582584..f74616a 100644 (file)
@@ -25,10 +25,14 @@ import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.MODEL;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
 import org.onap.cps.ncmp.api.models.PersistenceCmHandle;
+import org.onap.cps.ncmp.api.models.YangResource;
 import org.onap.cps.spi.model.ModuleReference;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.ResponseEntity;
@@ -55,12 +59,14 @@ public class DmiModelOperations extends DmiOperations {
      * @param persistenceCmHandle the persistence cm handle
      * @return module references
      */
-    public ResponseEntity<String> getModuleReferences(final PersistenceCmHandle persistenceCmHandle) {
+    public List<ModuleReference> getModuleReferences(final PersistenceCmHandle persistenceCmHandle) {
         final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
             .build();
         dmiRequestBody.asCmHandleProperties(persistenceCmHandle.getAdditionalProperties());
-        return getResourceFromDmiWithJsonData(persistenceCmHandle.resolveDmiServiceName(MODEL),
+        final ResponseEntity<Object> dmiFetchModulesResponseEntity = getResourceFromDmiWithJsonData(
+            persistenceCmHandle.resolveDmiServiceName(MODEL),
             getDmiRequestBodyAsString(dmiRequestBody), persistenceCmHandle.getId(), "modules");
+        return toModuleReferences((Map) dmiFetchModulesResponseEntity.getBody());
     }
 
     /**
@@ -68,17 +74,18 @@ public class DmiModelOperations extends DmiOperations {
      *
      * @param persistenceCmHandle the persistenceCmHandle
      * @param unknownModuleReferences the unknown module references
-     * @return yang resources
+     * @return yang resources as map of module name to yang(re)source
      */
-    public ResponseEntity<String>  getNewYangResourcesFromDmi(final PersistenceCmHandle persistenceCmHandle,
-                                                              final List<ModuleReference> unknownModuleReferences) {
+    public Map<String, String> getNewYangResourcesFromDmi(final PersistenceCmHandle persistenceCmHandle,
+                                                          final List<ModuleReference> unknownModuleReferences) {
         final String jsonDataWithDataAndCmHandleProperties = getRequestBodyToFetchYangResources(
             unknownModuleReferences, persistenceCmHandle.getAdditionalProperties());
-        return getResourceFromDmiWithJsonData(
+        final ResponseEntity<Object> responseEntity = getResourceFromDmiWithJsonData(
             persistenceCmHandle.resolveDmiServiceName(MODEL),
             jsonDataWithDataAndCmHandleProperties,
             persistenceCmHandle.getId(),
             "moduleResources");
+        return asModuleNameToYangResourceMap(responseEntity);
     }
 
     /**
@@ -90,7 +97,7 @@ public class DmiModelOperations extends DmiOperations {
      * @param resourceName name of the resource(s)
      * @return {@code ResponseEntity} response entity
      */
-    private ResponseEntity<String> getResourceFromDmiWithJsonData(final String dmiServiceName,
+    private ResponseEntity<Object> getResourceFromDmiWithJsonData(final String dmiServiceName,
                                                                   final String jsonData,
                                                                   final String cmHandle,
                                                                   final String resourceName) {
@@ -122,7 +129,6 @@ public class DmiModelOperations extends DmiOperations {
     }
 
     private static JsonObject toJsonObject(final List<PersistenceCmHandle.AdditionalProperty> cmHandleProperties) {
-        //TODO Toine/Joe Double check format with existing test data
         final JsonObject asJsonObject = new JsonObject();
         for (final PersistenceCmHandle.AdditionalProperty additionalProperty : cmHandleProperties) {
             asJsonObject.addProperty(additionalProperty.getName(), additionalProperty.getValue());
@@ -130,4 +136,34 @@ public class DmiModelOperations extends DmiOperations {
         return asJsonObject;
     }
 
+    private List<ModuleReference> toModuleReferences(final Map dmiFetchModulesResponseAsMap) {
+        final List<ModuleReference> moduleReferences = new ArrayList<>();
+
+        if (dmiFetchModulesResponseAsMap != null) {
+            final List moduleReferencesAsList = (List) dmiFetchModulesResponseAsMap.get("schemas");
+            if (moduleReferencesAsList != null) {
+                moduleReferencesAsList.forEach(moduleReferenceAsMap -> {
+                    final ModuleReference moduleReference =
+                        objectMapper.convertValue(moduleReferenceAsMap, ModuleReference.class);
+                    moduleReferences.add(moduleReference);
+                });
+            }
+        }
+        return moduleReferences;
+    }
+
+    private Map<String, String> asModuleNameToYangResourceMap(final ResponseEntity<Object> responseEntity) {
+        final Map<String, String> yangResourcesModuleNameToContentMap = new HashMap<>();
+        final List<Map<String, String>> yangResourcesAsList = (List) responseEntity.getBody();
+
+        if (yangResourcesAsList != null) {
+            yangResourcesAsList.forEach(yangResourceAsMap -> {
+                final YangResource yangResource =
+                    objectMapper.convertValue(yangResourceAsMap, YangResource.class);
+                yangResourcesModuleNameToContentMap.put(yangResource.getModuleName(),
+                    yangResource.getYangSource());
+            });
+        }
+        return yangResourcesModuleNameToContentMap;
+    }
 }
index a5c1f45..de60a01 100644 (file)
 
 package org.onap.cps.ncmp.api.impl
 
-
+import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
 import org.onap.cps.ncmp.api.models.PersistenceCmHandle
-import org.onap.cps.ncmp.utils.TestUtils
 import org.onap.cps.spi.model.ModuleReference
-import org.springframework.http.HttpStatus
-import org.springframework.http.ResponseEntity
 import spock.lang.Specification
 
 class NetworkCmProxyDataServiceImplModelSyncSpec extends Specification {
@@ -38,7 +35,7 @@ class NetworkCmProxyDataServiceImplModelSyncSpec extends Specification {
     def mockDmiModelOperations = Mock(DmiModelOperations)
 
     def objectUnderTest = new NetworkCmProxyDataServiceImpl(null, mockDmiModelOperations,
-        mockCpsModuleService, null, null, mockCpsAdminService, null)
+        mockCpsModuleService, null, null, mockCpsAdminService, new ObjectMapper())
 
     def expectedDataspaceName = 'NFP-Operational'
 
@@ -50,25 +47,24 @@ class NetworkCmProxyDataServiceImplModelSyncSpec extends Specification {
                 cmHandleForModelSync.asAdditionalProperties(additionalProperties)
             }
         and: 'dmi operations returns some module references'
-            def jsonData = TestUtils.getResourceFileContent('cmHandleModules.json')
-            def moduleReferencesFromCmHandleAsJson = new ResponseEntity<String>(jsonData, HttpStatus.OK)
-            mockDmiModelOperations.getModuleReferences(cmHandleForModelSync) >> moduleReferencesFromCmHandleAsJson
+            def moduleReferences =  [ new ModuleReference(moduleName:'module1',revision:'1'),
+                                                            new ModuleReference(moduleName:'module2',revision:'2') ]
+            mockDmiModelOperations.getModuleReferences(cmHandleForModelSync) >> moduleReferences
         and: 'CPS-Core returns list of existing module resources'
             mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> existingModuleResourcesInCps
         and: 'DMI-Plugin returns resource(s) for "new" module(s)'
-            def moduleResources = new ResponseEntity<String>(sdncReponseBody, HttpStatus.OK)
-            mockDmiModelOperations.getNewYangResourcesFromDmi(cmHandleForModelSync, [new ModuleReference('module1', '1')]) >> moduleResources
+            mockDmiModelOperations.getNewYangResourcesFromDmi(cmHandleForModelSync, [new ModuleReference('module1', '1')]) >> yangResourceToContentMap
         when: 'module sync is triggered'
             objectUnderTest.syncModulesAndCreateAnchor(cmHandleForModelSync)
         then: 'the CPS module service is called once with the correct parameters'
-            1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, cmHandleForModelSync.getId(), expectedYangResourceToContentMap, expectedKnownModules)
+            1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, cmHandleForModelSync.getId(), yangResourceToContentMap, expectedKnownModules)
         and: 'admin service create anchor method has been called with correct parameters'
             1 * mockCpsAdminService.createAnchor(expectedDataspaceName, cmHandleForModelSync.getId(), cmHandleForModelSync.getId())
         where: 'the following parameters are used'
-            scenario                        | additionalProperties | existingModuleResourcesInCps                                               | sdncReponseBody                                                                   || expectedYangResourceToContentMap | expectedKnownModules                                                       | expectedJsonForAdditionalProperties
-            'one unknown module'            | ['name1': 'value1']  | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | '[{"moduleName" : "module1", "revision" : "1","yangSource": "some yang source"}]' || [module1: 'some yang source']    | [new ModuleReference('module2', '2')]                                      | '{"name1":"value1"}'
-            'no add. properties'            | [:]                  | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | '[{"moduleName" : "module1", "revision" : "1","yangSource": "some yang source"}]' || [module1: 'some yang source']    | [new ModuleReference('module2', '2')]                                      | '{}'
-            'additional properties is null' | null                 | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | '[{"moduleName" : "module1", "revision" : "1","yangSource": "some yang source"}]' || [module1: 'some yang source']    | [new ModuleReference('module2', '2')]                                      | '{}'
-            'no unknown module'             | [:]                  | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | '[]'                                                                              || [:]                              | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | '{}'
+            scenario                        | additionalProperties | existingModuleResourcesInCps                                               | yangResourceToContentMap      || expectedKnownModules                                                       | expectedJsonForAdditionalProperties
+            'one unknown module'            | ['name1': 'value1']  | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')]                                      | '{"name1":"value1"}'
+            'no add. properties'            | [:]                  | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')]                                      | '{}'
+            'additional properties is null' | null                 | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')]                                      | '{}'
+            'no unknown module'             | [:]                  | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | [:]                           || [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | '{}'
     }
 }
index c396a2e..6249271 100644 (file)
 
 package org.onap.cps.ncmp.api.impl
 
-import org.onap.cps.ncmp.api.impl.client.DmiRestClient
-import org.onap.cps.ncmp.api.impl.operations.DmiRequestBody
-import org.springframework.http.HttpHeaders
-
 import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
 import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING
 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
@@ -40,7 +36,6 @@ import org.onap.cps.api.CpsModuleService
 import org.onap.cps.api.CpsQueryService
 import org.onap.cps.ncmp.api.impl.exception.NcmpException
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
-import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
 import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.model.DataNode
 import org.springframework.http.HttpStatus
index 8c46178..389086c 100644 (file)
@@ -46,7 +46,7 @@ class DmiRestClientSpec extends Specification {
     def 'DMI POST operation'() {
         given: 'the rest template returns a valid response entity'
             def mockResponseEntity = Mock(ResponseEntity)
-            mockRestTemplate.exchange(resourceUrl, HttpMethod.POST, _ as HttpEntity, String.class) >> mockResponseEntity
+            mockRestTemplate.exchange(resourceUrl, HttpMethod.POST, _ as HttpEntity, Object.class) >> mockResponseEntity
         when: 'POST operation is invoked'
             def result = objectUnderTest.postOperation(resourceUrl, new HttpHeaders())
         then: 'the output of the method is equal to the output from the rest template'
@@ -56,7 +56,7 @@ class DmiRestClientSpec extends Specification {
     def 'DMI POST operation with JSON.'() {
         given: 'the rest template returns a valid response entity'
             def mockResponseEntity = Mock(ResponseEntity)
-            mockRestTemplate.postForEntity(resourceUrl, _ as HttpEntity, String.class) >> mockResponseEntity
+            mockRestTemplate.postForEntity(resourceUrl, _ as HttpEntity, Object.class) >> mockResponseEntity
         when: 'POST operation is invoked'
             def result = objectUnderTest.postOperationWithJsonData(resourceUrl, 'json-data', new HttpHeaders())
         then: 'the output of the method is equal to the output from the test template'
index 2c4ba68..e1aba79 100644 (file)
@@ -22,6 +22,8 @@ package org.onap.cps.ncmp.api.impl.config
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.boot.web.client.RestTemplateBuilder
+import org.springframework.http.MediaType
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
 import org.springframework.test.context.ContextConfiguration
 import org.springframework.web.client.RestTemplate
 import spock.lang.Specification
@@ -33,6 +35,8 @@ class NcmpConfigurationSpec extends Specification{
     @Autowired
     NcmpConfiguration.DmiProperties dmiProperties
 
+    def mockRestTemplateBuilder = new RestTemplateBuilder()
+
     def 'NcmpConfiguration Construction.'() {
         expect: 'the system can create an instance'
              new NcmpConfiguration() != null
@@ -45,13 +49,14 @@ class NcmpConfigurationSpec extends Specification{
     }
 
     def 'Rest Template creation.'() {
-        given: 'a rest template builder'
-            def mockRestTemplateBuilder = Mock(RestTemplateBuilder)
-            def expectedRestTemplate = Mock(RestTemplate)
-            mockRestTemplateBuilder.build() >> expectedRestTemplate
         when: 'a rest template is created'
             def result = NcmpConfiguration.restTemplate(mockRestTemplateBuilder)
-        then: 'the rest template from the builder is returned'
-            assert result == expectedRestTemplate
+        then: 'the rest template is returned'
+            assert result instanceof RestTemplate
+        and: 'a jackson media converter has been added'
+            def lastMessageConverter = result.getMessageConverters().get(result.getMessageConverters().size()-1)
+            lastMessageConverter instanceof MappingJackson2HttpMessageConverter
+        and: 'the jackson media converters supports the expected media types'
+            lastMessageConverter.getSupportedMediaTypes() == [MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN];
     }
 }
index d9d1271..335bc06 100644 (file)
@@ -41,8 +41,41 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
     @Autowired
     DmiModelOperations objectUnderTest
 
-    def 'Module references for a persistence cm handle #scenario.'() {
-        given: 'a persistence cm handle for #cmHandleId'
+    def 'Retrieving module references.'() {
+        given: 'a persistence cm handle'
+            mockPersistenceCmHandleRetrieval([])
+        and: 'a positive response from dmi service when it is called with the expected parameters'
+            def moduleReferencesAsLisOfMaps = [[moduleName:'mod1',revision:'A'],[moduleName:'mod2',revision:'X']]
+            def responseFromDmi = new ResponseEntity([schemas:moduleReferencesAsLisOfMaps], HttpStatus.OK)
+            mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules",
+                '{"cmHandleProperties":{}}', [:]) >> responseFromDmi
+        when: 'get module references is called'
+            def result = objectUnderTest.getModuleReferences(persistenceCmHandle)
+        then: 'the result consists of expected module references'
+            assert result == [new ModuleReference(moduleName:'mod1',revision:'A'), new ModuleReference(moduleName:'mod2',revision:'X')]
+    }
+
+    def 'Retrieving module references edge case: #scenario.'() {
+        given: 'a persistence cm handle'
+            mockPersistenceCmHandleRetrieval([])
+        and: 'any response from dmi service when it is called with the expected parameters'
+            // TODO (toine): production code ignores any error code from DMI, this should be improved in future
+            def responseFromDmi = new ResponseEntity(bodyAsMap, HttpStatus.NO_CONTENT)
+            mockDmiRestClient.postOperationWithJsonData(*_) >> responseFromDmi
+        when: 'get module references is called'
+            def result = objectUnderTest.getModuleReferences(persistenceCmHandle)
+        then: 'the result is empty'
+            assert result == []
+        where: 'the dmi response body has the following content'
+            scenario       | bodyAsMap
+            'no modules'   | [schemas:[]]
+            'modules null' | [schemas:null]
+            'no schema'    | [something:'else']
+            'no body'      | null
+    }
+
+    def 'Retrieving module references, additional property handling:  #scenario.'() {
+        given: 'a persistence cm handle'
             mockPersistenceCmHandleRetrieval(additionalPropertiesObject)
         and: 'a positive response from dmi service when it is called with tha expected parameters'
             def responseFromDmi = new ResponseEntity<String>(HttpStatus.OK)
@@ -51,35 +84,69 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         when: 'a get module references is called'
             def result = objectUnderTest.getModuleReferences(persistenceCmHandle)
         then: 'the result is the response from dmi service'
-            assert result == responseFromDmi
-        where:
+            assert result == []
+        where: 'the following additional properties are used'
             scenario               | additionalPropertiesObject || expectedAdditionalPropertiesInRequest
             'with properties'      | [sampleAdditionalProperty] || '{"prop1":"val1"}'
-            'with null properties' | null                       || "{}"
-            'without properties'   | []                         || "{}"
+            'with null properties' | null                       || '{}'
+            'without properties'   | []                         || '{}'
     }
 
-    def 'New yang resources from dmi using persistence cm handle #scenario.'() {
-        given: 'a persistence cm handle for #cmHandleId'
-            mockPersistenceCmHandleRetrieval(additionalPropertiesObject)
+    def 'Retrieving yang resources.'() {
+        given: 'a persistence cm handle'
+            mockPersistenceCmHandleRetrieval([])
         and: 'a positive response from dmi service when it is called with tha expected parameters'
-            def responseFromDmi = new ResponseEntity<String>(HttpStatus.OK)
+            def responseFromDmi = new ResponseEntity([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source'],
+                                                      [moduleName: 'mod2', revision: 'C', yangSource: 'other yang source']], HttpStatus.OK)
+            def expectedModuleReferencesInRequest = '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
+            mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
+                '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', [:]) >> responseFromDmi
+        when: 'get new yang resources from dmi service'
+            def result = objectUnderTest.getNewYangResourcesFromDmi(persistenceCmHandle, newModuleReferences)
+        then: 'the result has the 2 expected yang (re)sources (order is not guaranteed)'
+            assert result.size() == 2
+            assert result.get('mod1') == 'some yang source'
+            assert result.get('mod2') == 'other yang source'
+    }
+
+    def 'Retrieving yang resources, edge case: scenario.'() {
+        given: 'a persistence cm handle'
+            mockPersistenceCmHandleRetrieval([])
+        and: 'a positive response from dmi service when it is called with tha expected parameters'
+            // TODO (toine): production code ignores any error code from DMI, this should be improved in future
+            def responseFromDmi = new ResponseEntity(responseFromDmiBody, HttpStatus.NO_CONTENT)
+            mockDmiRestClient.postOperationWithJsonData(*_) >> responseFromDmi
+        when: 'get new yang resources from dmi service'
+            def result = objectUnderTest.getNewYangResourcesFromDmi(persistenceCmHandle, newModuleReferences)
+        then: 'the result is empty'
+            assert result == [:]
+        where: 'the dmi response body has the following content'
+            scenario      | responseFromDmiBody
+            'empty array' | []
+            'null array'  | null
+    }
+
+    def 'Retrieving yang resources, additional property handling #scenario.'() {
+        given: 'a persistence cm handle'
+            mockPersistenceCmHandleRetrieval(additionalPropertiesObject)
+        and: 'a positive response from dmi service when it is called with the expected parameters'
+            def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK)
             mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
             '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":'+expectedAdditionalPropertiesInRequest+'}',
             [:]) >> responseFromDmi
         when: 'get new yang resources from dmi service'
             def result = objectUnderTest.getNewYangResourcesFromDmi(persistenceCmHandle, unknownModuleReferences)
         then: 'the result is the response from dmi service'
-            assert result == responseFromDmi
-        where:
+            assert result == [mod1:'some yang source']
+        where: 'the following additional properties are used'
             scenario                                | additionalPropertiesObject | unknownModuleReferences || expectedAdditionalPropertiesInRequest | expectedModuleReferencesInRequest
             'with module references and properties' | [sampleAdditionalProperty] | newModuleReferences     || '{"prop1":"val1"}'                    | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
             'without module references'             | [sampleAdditionalProperty] | []                      || '{"prop1":"val1"}'                    | ''
             'without properties'                    | []                         | newModuleReferences     || '{}'                                  | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
     }
 
-    def 'New yang resources from dmi with additional properties null'() {
-        given: 'a persistence cm handle for #cmHandleId'
+    def 'Retrieving yang resources from dmi with additional properties null.'() {
+        given: 'a persistence cm handle'
             mockPersistenceCmHandleRetrieval(null)
         when: 'a get new yang resources from dmi is called'
             objectUnderTest.getNewYangResourcesFromDmi(persistenceCmHandle, [])
@@ -87,8 +154,8 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
             thrown(NullPointerException)
     }
 
-    def 'Json Processing Exception'() {
-        given: 'a persistence cm handle for #cmHandleId'
+    def 'Retrieving module references with Json processing exception.'() {
+        given: 'a persistence cm handle'
             mockPersistenceCmHandleRetrieval([])
         and: 'a Json processing exception occurs'
             spyObjectMapper.writeValueAsString(_) >> {throw (new JsonProcessingException(''))}
@@ -97,7 +164,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         then: 'an ncmp exception is thrown'
             def exceptionThrown = thrown(NcmpException)
         and: 'the message indicates a parsing error'
-            exceptionThrown.message.toLowerCase().contains("parsing error")
+            exceptionThrown.message.toLowerCase().contains('parsing error')
     }
 
 }
index 75f0af4..f1939cf 100755 (executable)
 # Branched from ccsdk/distribution to this repository Feb 23, 2021
 #
 
+echo '================================== CPS-NCMP Logs ========================'
+docker logs cps-and-ncmp
+
+echo '================================== DMI Logs ============================='
+docker logs ncmp-dmi-plugin
+
 echo 'Stopping, Removing all running containers...'
 docker stop $(docker ps -aq) && docker rm $(docker ps -aq)
 
index 51eabc8..1673baa 100644 (file)
@@ -100,7 +100,7 @@ Verify update to bookstore using passthrough-running did not remove category 02
 Delete Bookstore using passthrough-running for Category 01
     ${uri}=              Set Variable       ${ncmpBasePath}/v1/ch/PNFDemo/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=stores:bookstore/categories=01
     ${headers}=          Create Dictionary  Content-Type=application/json   Authorization=${auth}
-    ${response}=         DELETE On Session     CPS_URL   ${uri}   headers=${headers}   data=''
+    ${response}=         DELETE On Session  CPS_URL   ${uri}   headers=${headers}   data={}
     Should Be Equal As Strings              ${response.status_code}   204
 
 Verify delete to bookstore using passthrough-running removed only category 01
index da96970..0ee8b1b 100755 (executable)
@@ -26,11 +26,11 @@ This section lists the main changes & fixes merged into master (snapshot) versio
 
 Features
 --------
-
    - `CPS-559 <https://jira.onap.org/browse/CPS-559>`_  Define response objects (schemas) in cps-ncmp
    - `CPS-636 <https://jira.onap.org/browse/CPS-636>`_  Update operation for datastore pass through running
    - `CPS-638 <https://jira.onap.org/browse/CPS-638>`_  Delete operation for datastore pass through running
    - `CPS-741 <https://jira.onap.org/browse/CPS-741>`_  Re sync after removing cm handles
+   - `CPS-777 <https://jira.onap.org/browse/CPS-777>`_  Ensure all DMI operations use POST method
    - `CPS-780 <https://jira.onap.org/browse/CPS-780>`_  Add examples for parameters, request and response in openapi yaml for cps-core
 
 Bug Fixes