Fix conversion to string for result from YangResource request on SDN-C 22/126022/3
authorToineSiebelink <toine.siebelink@est.tech>
Thu, 2 Dec 2021 13:53:24 +0000 (13:53 +0000)
committerToineSiebelink <toine.siebelink@est.tech>
Fri, 3 Dec 2021 10:14:54 +0000 (10:14 +0000)
-Extracted out relevant method to separate class
-Added comprehensive tests around relevant methods

Issue-ID: CPS-788

Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
Change-Id: Icb2104ca606b28f1904ead368bcde27f578e7e53

src/main/java/org/onap/cps/ncmp/dmi/service/DmiServiceImpl.java
src/main/java/org/onap/cps/ncmp/dmi/service/YangResourceExtractor.java [new file with mode: 0644]
src/test/groovy/org/onap/cps/ncmp/dmi/service/DmiServiceImplSpec.groovy
src/test/groovy/org/onap/cps/ncmp/dmi/service/YangResourceExtractorSpec.groovy [new file with mode: 0644]

index 99127e6..27a292d 100644 (file)
@@ -23,12 +23,12 @@ package org.onap.cps.ncmp.dmi.service;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
+import com.fasterxml.jackson.databind.ObjectWriter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.dmi.config.DmiPluginConfig.DmiPluginProperties;
 import org.onap.cps.ncmp.dmi.exception.CmHandleRegistrationException;
@@ -61,7 +61,6 @@ public class DmiServiceImpl implements DmiService {
     private DmiPluginProperties dmiPluginProperties;
     private static final String RESPONSE_CODE = "response code : ";
     private static final String MESSAGE = " message : ";
-    private static final String IETF_NETCONF_MONITORING_OUTPUT = "ietf-netconf-monitoring:output";
 
     /**
      * Constructor.
@@ -96,11 +95,12 @@ public class DmiServiceImpl implements DmiService {
     @Override
     public YangResources getModuleResources(final String cmHandle, final List<ModuleReference> moduleReferences) {
         final YangResources yangResources = new YangResources();
-        for (final var moduleReference : moduleReferences) {
-            final var moduleRequest = createModuleRequest(moduleReference);
+        for (final ModuleReference moduleReference : moduleReferences) {
+            final String moduleRequest = createModuleRequest(moduleReference);
             final ResponseEntity<String> responseEntity = sdncOperations.getModuleResource(cmHandle, moduleRequest);
             if (responseEntity.getStatusCode() == HttpStatus.OK) {
-                yangResources.add(toYangResource(moduleReference, responseEntity));
+                final YangResource yangResource = YangResourceExtractor.toYangResource(moduleReference, responseEntity);
+                yangResources.add(yangResource);
             } else if (responseEntity.getStatusCode() == HttpStatus.NOT_FOUND) {
                 log.error("SDNC did not return a module resource for the given cmHandle {}", cmHandle);
                 throw new ModuleResourceNotFoundException(cmHandle,
@@ -140,7 +140,7 @@ public class DmiServiceImpl implements DmiService {
     }
 
     private ModuleSetSchemas toModuleSetSchemas(final ModuleSchema moduleSchema) {
-        final var moduleSetSchemas = new ModuleSetSchemas();
+        final ModuleSetSchemas moduleSetSchemas = new ModuleSetSchemas();
         moduleSetSchemas.setModuleName(moduleSchema.getIdentifier());
         moduleSetSchemas.setNamespace(moduleSchema.getNamespace());
         moduleSetSchemas.setRevision(moduleSchema.getVersion());
@@ -186,13 +186,13 @@ public class DmiServiceImpl implements DmiService {
     }
 
     private String createModuleRequest(final ModuleReference moduleReference) {
-        final var ietfNetconfModuleReferences = new LinkedHashMap<>();
+        final Map ietfNetconfModuleReferences = new LinkedHashMap<String, String>();
         ietfNetconfModuleReferences.put("ietf-netconf-monitoring:identifier", moduleReference.getName());
         ietfNetconfModuleReferences.put("ietf-netconf-monitoring:version", moduleReference.getRevision());
-        final var writer = objectMapper.writer().withRootName("ietf-netconf-monitoring:input");
+        final ObjectWriter objectWriter = objectMapper.writer().withRootName("ietf-netconf-monitoring:input");
         final String moduleRequest;
         try {
-            moduleRequest = writer.writeValueAsString(ietfNetconfModuleReferences);
+            moduleRequest = objectWriter.writeValueAsString(ietfNetconfModuleReferences);
         } catch (final JsonProcessingException e) {
             log.error("JSON exception occurred when creating the module request for the given module reference {}",
                 moduleReference.getName());
@@ -202,25 +202,4 @@ public class DmiServiceImpl implements DmiService {
         return moduleRequest;
     }
 
-    private YangResource toYangResource(final ModuleReference moduleReference,
-        final ResponseEntity<String> response) {
-        final YangResource yangResource = new YangResource();
-        yangResource.setModuleName(moduleReference.getName());
-        yangResource.setRevision(moduleReference.getRevision());
-        yangResource.setYangSource(extractYangSourceFromBody(response));
-        return yangResource;
-    }
-
-    private String extractYangSourceFromBody(final ResponseEntity<String> responseEntity) {
-        final var responseBodyAsJsonObject = new Gson().fromJson(responseEntity.getBody(), JsonObject.class);
-        if (responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT) == null
-            || responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT)
-            .getAsJsonPrimitive("data") == null) {
-            log.error("Error occurred when trying to parse the response body from sdnc {}", responseEntity.getBody());
-            throw new ModuleResourceNotFoundException(responseEntity.getBody(),
-                "Error occurred when trying to parse the response body from sdnc.");
-        }
-        return responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT).getAsJsonPrimitive("data")
-            .toString();
-    }
 }
diff --git a/src/main/java/org/onap/cps/ncmp/dmi/service/YangResourceExtractor.java b/src/main/java/org/onap/cps/ncmp/dmi/service/YangResourceExtractor.java
new file mode 100644 (file)
index 0000000..d6ff3d2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.service;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException;
+import org.onap.cps.ncmp.dmi.model.YangResource;
+import org.onap.cps.ncmp.dmi.service.model.ModuleReference;
+import org.springframework.http.ResponseEntity;
+
+@Slf4j
+public class YangResourceExtractor {
+    static YangResource toYangResource(final ModuleReference moduleReference,
+                                       final ResponseEntity<String> responseEntity) {
+        final YangResource yangResource = new YangResource();
+        yangResource.setModuleName(moduleReference.getName());
+        yangResource.setRevision(moduleReference.getRevision());
+        yangResource.setYangSource(extractYangSourceFromBody(responseEntity));
+        return yangResource;
+    }
+
+    private static String extractYangSourceFromBody(final ResponseEntity<String> responseEntity) {
+        final JsonObject responseBodyAsJsonObject = new Gson().fromJson(responseEntity.getBody(), JsonObject.class);
+        final JsonObject monitoringOutputAsJsonObject =
+            responseBodyAsJsonObject.getAsJsonObject("ietf-netconf-monitoring:output");
+        if (monitoringOutputAsJsonObject == null
+            || monitoringOutputAsJsonObject.getAsJsonPrimitive("data") == null) {
+            log.error("Error occurred when trying to parse the response body from sdnc {}", responseEntity.getBody());
+            throw new ModuleResourceNotFoundException(responseEntity.getBody(),
+                "Error occurred when trying to parse the response body from sdnc.");
+        }
+        return monitoringOutputAsJsonObject.getAsJsonPrimitive("data").getAsString();
+    }
+
+}
index a463a34..edf1a80 100644 (file)
@@ -139,8 +139,8 @@ class DmiServiceImplSpec extends Specification {
                                                                        new ResponseEntity<String>('{"ietf-netconf-monitoring:output": {"data": "some-data2"}}', HttpStatus.OK)]
         and: 'the result is a yang resources object with the expected names, revisions and yang-sources'
             def yangResources = new YangResources()
-            def yangResource1 = new YangResource(yangSource: '"some-data1"', moduleName: 'name-1', revision: 'revision-1')
-            def yangResource2 = new YangResource(yangSource: '"some-data2"', moduleName: 'name-2', revision: 'revision-2')
+            def yangResource1 = new YangResource(yangSource: 'some-data1', moduleName: 'name-1', revision: 'revision-1')
+            def yangResource2 = new YangResource(yangSource: 'some-data2', moduleName: 'name-2', revision: 'revision-2')
             yangResources.add(yangResource1)
             yangResources.add(yangResource2)
             assert result == yangResources
diff --git a/src/test/groovy/org/onap/cps/ncmp/dmi/service/YangResourceExtractorSpec.groovy b/src/test/groovy/org/onap/cps/ncmp/dmi/service/YangResourceExtractorSpec.groovy
new file mode 100644 (file)
index 0000000..656cfcb
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.service
+
+import com.google.gson.JsonSyntaxException
+import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException
+import org.onap.cps.ncmp.dmi.service.model.ModuleReference
+import org.springframework.http.ResponseEntity
+import spock.lang.Specification
+
+class YangResourceExtractorSpec extends Specification {
+
+    static def BACK_SLASH = '\\';
+    static def NEW_LINE = '\n';
+    static def QUOTE = '"';
+    static def TAB = '\t';
+
+    static def YANG_ESCAPED_NEW_LINE = '\\n'
+    static def YANG_ESCAPED_BACK_SLASH = '\\\\'
+    static def YANG_ESCAPED_QUOTE = '\\"'
+    static def YANG_ESCAPED_TAB = '\\t'
+
+    static def SDNC_OUTPUT_JSON_NAME = '"ietf-netconf-monitoring:output"'
+
+    def moduleReference = new ModuleReference(name: 'test', revision: 'rev')
+    def responseEntity = Mock(ResponseEntity)
+
+    def 'Extract yang resource with escaped characters in the source.'() {
+        given: 'a response entity with a data field of value #jsonValue'
+            responseEntity.getBody() >> '{' + SDNC_OUTPUT_JSON_NAME + ': { "data": "' + jsonValue + '" }}'
+        when: 'the yang resource is extracted'
+            def result = YangResourceExtractor.toYangResource(moduleReference, responseEntity)
+        then: 'the yang source string is as expected'
+            result.getYangSource() == expectedString
+        where: 'the following data is used'
+            jsonValue                                 || expectedString
+            'line1' + YANG_ESCAPED_NEW_LINE + 'line2' || 'line1' + NEW_LINE + 'line2'
+            'a' + YANG_ESCAPED_BACK_SLASH+'b'         || 'a'+BACK_SLASH +'b'
+            'a' + YANG_ESCAPED_QUOTE + 'b'            || 'a'+QUOTE+'b'
+            'a' + YANG_ESCAPED_TAB + 'b'              || 'a'+TAB+'b'
+    }
+
+    def 'Extract yang resource with escaped characters in the source inside escaped double quotes.'() {
+        given: 'a response entity with a data field of value #jsonValue wrapped in escaped double quotes'
+            responseEntity.getBody() >> '{' + SDNC_OUTPUT_JSON_NAME + ': { "data": "' + YANG_ESCAPED_QUOTE + jsonValue + YANG_ESCAPED_QUOTE + '" }}'
+        when: 'the yang resource is extracted'
+            def result = YangResourceExtractor.toYangResource(moduleReference, responseEntity)
+        then: 'the yang source string is as expected'
+            result.getYangSource() == expectedString
+        where: 'the following data is used'
+            jsonValue                                 || expectedString
+            'line1' + YANG_ESCAPED_NEW_LINE + 'line2' || '"line1' + NEW_LINE + 'line2"'
+            'a' + YANG_ESCAPED_BACK_SLASH+'b'         || '"a'+BACK_SLASH +'b"'
+            'a' + YANG_ESCAPED_QUOTE + 'b'            || '"a'+QUOTE+'b"'
+            'a' + YANG_ESCAPED_TAB + 'b'              || '"a'+TAB+'b"'
+    }
+
+    def 'Attempt to extract yang resource with un-escaped double quotes in the source.'() {
+        given: 'a response entity with a data field with unescaped double quotes'
+            responseEntity.getBody() >> '{' + SDNC_OUTPUT_JSON_NAME + ': { "data": "' + QUOTE + 'some data' + QUOTE + '" }}'
+        when: 'Json is converted to String'
+            YangResourceExtractor.toYangResource(moduleReference, responseEntity)
+        then: 'the output of the method is equal to the output from the test template'
+            thrown(JsonSyntaxException)
+    }
+
+    def 'Attempt to extract yang resource without #without.'() {
+        given: 'a response entity with a body without #without'
+            responseEntity.getBody() >> jsonBody
+        when: 'Json is converted to String'
+            YangResourceExtractor.toYangResource(moduleReference, responseEntity)
+        then: 'the output of the method is equal to the output from the test template'
+            thrown(ModuleResourceNotFoundException)
+        where:
+            without               | jsonBody
+            'data'                | '{' + SDNC_OUTPUT_JSON_NAME + ': { "something": "else" }}'
+            SDNC_OUTPUT_JSON_NAME | '{"something:else": { "data": "data" }}'
+    }
+
+}